Project

General

Profile

1 155 jones
/**
2 203 jones
 *  '$RCSfile$'
3 2093 tao
 *    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 203 jones
 *             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 155 jones
 *
11 203 jones
 *   '$Author$'
12
 *     '$Date$'
13
 * '$Revision$'
14 669 jones
 *
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 155 jones
 */
29
30
package edu.ucsb.nceas.metacat;
31
32 2067 jones
import java.io.IOException;
33
import java.io.Reader;
34
import java.io.StringReader;
35 6602 leinfelder
import java.util.ArrayList;
36 2067 jones
import java.util.Enumeration;
37 6602 leinfelder
import java.util.List;
38 155 jones
import java.util.Stack;
39 158 jones
import java.util.Vector;
40 155 jones
41 5015 daigle
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
42 4698 daigle
import edu.ucsb.nceas.metacat.util.MetacatUtil;
43 4812 daigle
import edu.ucsb.nceas.metacat.util.SystemUtil;
44 4854 daigle
//import edu.ucsb.nceas.utilities.UtilException;
45 2067 jones
46 2663 sgarg
import org.apache.log4j.Logger;
47 185 jones
import org.xml.sax.Attributes;
48 158 jones
import org.xml.sax.InputSource;
49
import org.xml.sax.SAXException;
50 185 jones
import org.xml.sax.XMLReader;
51 2067 jones
import org.xml.sax.helpers.DefaultHandler;
52 185 jones
import org.xml.sax.helpers.XMLReaderFactory;
53 2419 sgarg
import java.util.Iterator;
54 155 jones
55 402 berkley
/**
56 2067 jones
 * A Class that represents a structured query, and can be constructed from an
57
 * XML serialization conforming to
58 2093 tao
 *
59 2067 jones
 * @see pathquery.dtd. The printSQL() method can be used to print a SQL
60
 *      serialization of the query.
61 155 jones
 */
62 2067 jones
public class QuerySpecification extends DefaultHandler
63
{
64 1832 tao
65 2067 jones
    /** flag determining whether extended query terms are present */
66
    private boolean containsExtendedSQL = false;
67 158 jones
68 3235 sledge
    /** flag determining whether predicates are present */
69
    private boolean containsPredicates = false;
70
71 2067 jones
    /** Identifier for this query document */
72
    private String meta_file_id;
73 158 jones
74 2067 jones
    /** 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 3769 tao
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 2067 jones
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 3766 tao
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 2067 jones
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 3235 sledge
    public static final char PREDICATE_START = '[';
126
127
    public static final char PREDICATE_END = ']';
128
129 3308 tao
    //private boolean hasAttributeReturnField = false;
130 2067 jones
131 3308 tao
    //private Hashtable attributeReturnList = new Hashtable();
132 2067 jones
133 3308 tao
    //private int countAttributeReturnField = 0;
134 2067 jones
135 2093 tao
    private StringBuffer textBuffer = new StringBuffer();
136 3223 tao
137
138 2663 sgarg
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
139
140 2067 jones
    /**
141
     * construct an instance of the QuerySpecification class
142 2093 tao
     *
143 2067 jones
     * @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 3769 tao
        returnFieldListWithPredicates = new Vector();
162 2067 jones
        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 5311 daigle
        	logMetacat.error("QuerySpecification() - SAX parser not instantiated properly.");
170 2067 jones
        }
171
        try {
172
            parser.parse(new InputSource(queryspec));
173 5311 daigle
        } catch (SAXException se) {
174
            logMetacat.error("QuerySpecification() - SAX error parsing data: " + se.getMessage());
175 2067 jones
        }
176 181 jones
    }
177 2067 jones
178
    /**
179
     * construct an instance of the QuerySpecification class
180 2093 tao
     *
181 2067 jones
     * @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 155 jones
    }
193
194 2067 jones
    /**
195
     * construct an instance of the QuerySpecification class which don't need
196
     * to parser a xml document
197 2093 tao
     *
198 2067 jones
     * @param accNumberSeparator
199
     *            the separator between doc version
200
     */
201
    public QuerySpecification(String accNumberSeparator) throws IOException
202 2045 tao
    {
203 2067 jones
        // 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 3769 tao
        returnFieldListWithPredicates = new Vector();
210 2067 jones
        ownerList = new Vector();
211
        this.accNumberSeparator = accNumberSeparator;
212 2045 tao
    }
213 2067 jones
214
    /**
215
     * Method to set user name
216 2093 tao
     *
217 2067 jones
     * @param myName
218
     *            the user name
219
     */
220
    public void setUserName(String myName)
221 2045 tao
    {
222 2067 jones
        //to lower case
223
        if (myName != null) {
224
            this.userName = myName.toLowerCase();
225
        } else {
226
            this.userName = myName;
227
        }
228 2045 tao
    }
229 2067 jones
230
    /**
231
     * Method to set user group
232 2093 tao
     *
233 2067 jones
     * @param myGroup
234
     *            the user group
235
     */
236
    public void setGroup(String[] myGroup)
237 1301 tao
    {
238 2067 jones
        this.group = myGroup;
239 1301 tao
    }
240 2067 jones
241
    /**
242
     * Method to indicate this query is a percentage search
243
     */
244
    public boolean isPercentageSearch()
245 1301 tao
    {
246 2067 jones
        return percentageSearch;
247 1301 tao
    }
248 2067 jones
249
    /*
250
     * Method to get owner query. If it is owner it has all permission
251
     */
252 7407 leinfelder
    private String createOwnerQuery()
253 1301 tao
    {
254 2067 jones
        String ownerQuery = null;
255 3223 tao
        //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 2067 jones
        }
263 5311 daigle
        logMetacat.info("QuerySpecification.createOwerQuery - OwnerQuery: " + ownerQuery);
264 2067 jones
        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 1301 tao
    {
273 2067 jones
        String allowQuery = null;
274
        String allowString = constructAllowString();
275 7407 leinfelder
        allowQuery = "SELECT guid from xml_access  " +
276
        		"WHERE ( " + allowString;
277 3312 tao
        allowQuery = allowQuery + ")";
278 5311 daigle
        logMetacat.info("QuerySpecification.createAllowRuleQuery - allow query is: " + allowQuery);
279 2067 jones
        return allowQuery;
280
281 1301 tao
    }
282 2067 jones
283
    /* Method to construct a allow rule string */
284
    private String constructAllowString()
285 1301 tao
    {
286 2067 jones
        String allowQuery = "";
287 3313 tao
288
       // add public
289
        allowQuery = "(lower(principal_name) = '" + PUBLIC
290
                + "'";
291
292
        // add user name
293
        if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
294
            allowQuery = allowQuery + "OR lower(principal_name) = '" + userName +"'";
295
296 2067 jones
        }
297 3313 tao
        // add  group
298 2067 jones
        if (group != null) {
299
            for (int i = 0; i < group.length; i++) {
300
                String groupUint = group[i];
301
                if (groupUint != null && !groupUint.equals("")) {
302
                    groupUint = groupUint.toLowerCase();
303 3313 tao
                    allowQuery = allowQuery + " OR lower(principal_name) = '"
304
                            + groupUint + "'";
305 2067 jones
                }//if
306
            }//for
307 1301 tao
        }//if
308 3313 tao
        // add allow rule
309
        allowQuery = allowQuery + ") AND perm_type = 'allow'" + " AND permission > 3";
310 5311 daigle
        logMetacat.info("QuerySpecification.constructAllowString - allow string is: " + allowQuery);
311 2067 jones
        return allowQuery;
312
    }
313 155 jones
314 2067 jones
    /*
315
     * Method to create query for xml_access, this part is to get docid list
316
     * which have a deny rule and perm_order is allowFirst for a given user.
317
     * This means the user will be denied to read
318
     */
319
    private String createDenyRuleQuery()
320
    {
321
        String denyQuery = null;
322
        String denyString = constructDenyString();
323 7407 leinfelder
        denyQuery = "SELECT guid from xml_access " +
324
        		"WHERE ( " + denyString;
325 3312 tao
        denyQuery = denyQuery + ") ";
326 5311 daigle
        logMetacat.info("QuerySpecification.createDenyRuleQuery - denyquery is: " + denyQuery);
327 2067 jones
        return denyQuery;
328 711 jones
329 2067 jones
    }
330 181 jones
331 2067 jones
    /* Construct deny string */
332
    private String constructDenyString()
333 402 berkley
    {
334 2067 jones
        String denyQuery = "";
335 3313 tao
336
        // add public
337
        denyQuery = "(lower(principal_name) = '" + PUBLIC
338
                 + "'";
339
340
         // add user name
341
         if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
342
        	 denyQuery = denyQuery + "OR lower(principal_name) = '" + userName +"'";
343
344
         }
345
         // add  groups
346
         if (group != null) {
347
             for (int i = 0; i < group.length; i++) {
348
                 String groupUint = group[i];
349
                 if (groupUint != null && !groupUint.equals("")) {
350
                     groupUint = groupUint.toLowerCase();
351
                     denyQuery = denyQuery + " OR lower(principal_name) = '"
352
                             + groupUint + "'";
353
                 }//if
354
             }//for
355
         }//if
356
         // add deny rules
357
         denyQuery = denyQuery + ") AND perm_type = 'deny'" +  " AND perm_order ='allowFirst'" +" AND permission > 3";
358 5311 daigle
         logMetacat.info("QuerySpecification.constructDenyString - deny string is: " + denyQuery);
359 3313 tao
         return denyQuery;
360
361 402 berkley
    }
362 2067 jones
363
    /**
364
     * Method to append a access control query to SQL. So in DBQuery class, we
365
     * can get docid from both user specified query and access control query.
366
     * We don't need to checking permission after we get the doclist. It will
367
     * be good to performance
368 2093 tao
     *
369 2067 jones
     */
370
    public String getAccessQuery()
371 402 berkley
    {
372 2067 jones
        String accessQuery = null;
373 7407 leinfelder
        String owner = createOwnerQuery();
374 2067 jones
        String allow = createAllowRuleQuery();
375
        String deny = createDenyRuleQuery();
376 5311 daigle
377 7407 leinfelder
        if (owner != null)
378 3223 tao
        {
379 7407 leinfelder
          accessQuery = " AND (xml_documents.docid IN (" + owner + ")";
380
          accessQuery = accessQuery + " OR (identifier.guid IN (" + allow + ")"
381
                + " AND identifier.guid NOT IN (" + deny + ")))";
382 3223 tao
        }
383
        else
384
        {
385 7407 leinfelder
        	accessQuery = " AND (identifier.guid IN (" + allow + ")"
386
                + " AND identifier.guid NOT IN (" + deny + "))";
387 3223 tao
        }
388 5311 daigle
        logMetacat.info("QuerySpecification.getAccessQuery - access query is: " + accessQuery);
389 2067 jones
        return accessQuery;
390 402 berkley
    }
391 745 jones
392 2067 jones
    /**
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 745 jones
405 3308 tao
406 2067 jones
    /**
407
     * Accessor method to return the identifier of this Query
408
     */
409
    public String getIdentifier()
410
    {
411
        return meta_file_id;
412
    }
413 155 jones
414 2067 jones
    /**
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 745 jones
422 2067 jones
    /**
423
     * Accessor method to return the title of this Query
424
     */
425
    public String getQueryTitle()
426
    {
427
        return queryTitle;
428
    }
429 745 jones
430 2067 jones
    /**
431
     * method to set the title of this query
432
     */
433
    public void setQueryTitle(String title)
434
    {
435
        this.queryTitle = title;
436
    }
437 745 jones
438 2067 jones
    /**
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 745 jones
447 2067 jones
    /**
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 745 jones
455 2067 jones
    /**
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 172 jones
464 2067 jones
    /**
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 155 jones
472 2067 jones
    /**
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 155 jones
481 2067 jones
    /**
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 155 jones
489 2067 jones
    /**
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 155 jones
498 2067 jones
    /**
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 155 jones
    }
505
506 2067 jones
    /**
507
     * get the QueryGroup used to express query constraints
508
     */
509
    public QueryGroup getQueryGroup()
510
    {
511
        return query;
512 158 jones
    }
513 155 jones
514 2067 jones
    /**
515
     * set the querygroup
516
     */
517
    public void setQueryGroup(QueryGroup group)
518
    {
519
        query = group;
520 158 jones
    }
521
522 2067 jones
    /**
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 158 jones
531 2067 jones
    /**
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 5311 daigle
            logMetacat.error("QuerySpecification.getAccessQuery - Error: " + e.getMessage());
552 2067 jones
        }
553
554
        return parser;
555 1833 tao
    }
556 170 jones
557 2067 jones
    /**
558
     * callback method used by the SAX Parser when the start tag of an element
559
     * is detected. Used in this context to parse and store the query
560
     * information in class variables.
561
     */
562
    public void startElement(String uri, String localName, String qName,
563
            Attributes atts) throws SAXException
564
    {
565 5311 daigle
        logMetacat.debug("QuerySpecification.startElement - start element " + localName);
566 2067 jones
        BasicNode currentNode = new BasicNode(localName);
567 3766 tao
        //write element name into xml buffer.
568
        xml.append("<");
569
        xml.append(localName);
570 2067 jones
        // add attributes to BasicNode here
571
        if (atts != null) {
572
            int len = atts.getLength();
573
            for (int i = 0; i < len; i++) {
574
                currentNode
575
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
576 3766 tao
                xml.append(" ");
577
                xml.append(atts.getLocalName(i));
578
                xml.append("=\"");
579
                xml.append(atts.getValue(i));
580
                xml.append("\"");
581 2067 jones
            }
582
        }
583 3766 tao
        xml.append(">");
584 170 jones
585 2067 jones
        elementStack.push(currentNode);
586
        if (currentNode.getTagName().equals("querygroup")) {
587
            QueryGroup currentGroup = new QueryGroup(currentNode
588
                    .getAttribute("operator"));
589
            if (query == null) {
590
                query = currentGroup;
591
            } else {
592
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
593
                parentGroup.addChild(currentGroup);
594
            }
595
            queryStack.push(currentGroup);
596
        }
597 5311 daigle
        logMetacat.debug("QuerySpecification.startElement - ending startElement " + localName);
598 2067 jones
    }
599 172 jones
600 2067 jones
    /**
601
     * callback method used by the SAX Parser when the end tag of an element is
602
     * detected. Used in this context to parse and store the query information
603
     * in class variables.
604
     */
605
    public void endElement(String uri, String localName, String qName)
606
            throws SAXException
607
    {
608 5311 daigle
    	 logMetacat.debug("QuerySpecification.endElement - endElement "+localName);
609 2067 jones
        BasicNode leaving = (BasicNode) elementStack.pop();
610
        if (leaving.getTagName().equals("queryterm")) {
611
            boolean isCaseSensitive = (new Boolean(leaving
612
                    .getAttribute("casesensitive"))).booleanValue();
613
            QueryTerm currentTerm = null;
614
            if (currentPathexpr == null) {
615
                currentTerm = new QueryTerm(isCaseSensitive, leaving
616
                        .getAttribute("searchmode"), currentValue);
617
            } else {
618
                currentTerm = new QueryTerm(isCaseSensitive, leaving
619
                        .getAttribute("searchmode"), currentValue,
620
                        currentPathexpr);
621
            }
622
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
623
            currentGroup.addChild(currentTerm);
624
            currentValue = null;
625
            currentPathexpr = null;
626
        } else if (leaving.getTagName().equals("querygroup")) {
627
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
628 2093 tao
        } else if (leaving.getTagName().equals("meta_file_id")) {
629
              meta_file_id = textBuffer.toString().trim();
630
        } else if (leaving.getTagName().equals("querytitle")) {
631
              queryTitle = textBuffer.toString().trim();
632
        } else if (leaving.getTagName().equals("value")) {
633
              currentValue = textBuffer.toString().trim();
634 4698 daigle
              currentValue = MetacatUtil.normalize(currentValue);
635 2093 tao
        } else if (leaving.getTagName().equals("pathexpr")) {
636
              currentPathexpr = textBuffer.toString().trim();
637
        } else if (leaving.getTagName().equals("returndoctype")) {
638
              returnDocList.add(textBuffer.toString().trim());
639
        } else if (leaving.getTagName().equals("filterdoctype")) {
640
              filterDocList.add(textBuffer.toString().trim());
641
        } else if (leaving.getTagName().equals("returnfield")) {
642
              handleReturnField(textBuffer.toString().trim());
643
        } else if (leaving.getTagName().equals("filterdoctype")) {
644
              filterDocList.add(textBuffer.toString().trim());
645
        } else if (leaving.getTagName().equals("owner")) {
646
              ownerList.add(textBuffer.toString().trim());
647 172 jones
        }
648 3766 tao
        String normalizedXML = textBuffer.toString().trim();
649 5311 daigle
        logMetacat.debug("QuerySpecification.endElement - before normalize: " + normalizedXML);
650 4698 daigle
        normalizedXML =  MetacatUtil.normalize(normalizedXML);
651 5311 daigle
        logMetacat.debug("QuerySpecification.endElement - after normalize " + normalizedXML);
652 3766 tao
        xml.append(normalizedXML);
653
        xml.append("</");
654
        xml.append(localName);
655
        xml.append(">");
656 2093 tao
        //rest textBuffer
657
        textBuffer = new StringBuffer();
658
659 172 jones
    }
660 3766 tao
661
    /**
662
     * Gets normailized query string in xml format, which can be transformed
663
     * to html
664
     */
665
    public String getNormalizedXMLQuery()
666
    {
667
    	//System.out.println("normailized xml \n"+xml.toString());
668
    	return xml.toString();
669
    }
670
671 743 jones
672 2067 jones
    /**
673
     * callback method used by the SAX Parser when the text sequences of an xml
674
     * stream are detected. Used in this context to parse and store the query
675
     * information in class variables.
676
     */
677
    public void characters(char ch[], int start, int length)
678
    {
679 2093 tao
      // buffer all text nodes for same element. This is for text was splited
680
      // into different nodes
681 3766 tao
      String text = new String(ch, start, length);
682 5311 daigle
      logMetacat.debug("QuerySpecification.characters - the text in characters " + text);
683 3766 tao
      textBuffer.append(text);
684 2067 jones
685
    }
686
687 3358 tao
   /**
688
    * Method to handle return field. It will be callied in ecogrid part
689
    * @param inputString
690
    */
691
    public void handleReturnField(String inputString)
692 3235 sledge
    {
693
        int attributePos = inputString.indexOf(ATTRIBUTESYMBOL);
694
        int predicateStart = -1;
695
        int predicateEnd;
696
        boolean hasPredicate = false;
697 535 jones
698 3235 sledge
        while (true)
699
        {
700
            predicateStart = inputString.indexOf(PREDICATE_START, predicateStart + 1);
701
702
            if (attributePos == -1)
703
                break;
704
705
            if (predicateStart == -1)
706
                break;
707
708
            hasPredicate = true;
709
710
            if (attributePos < predicateStart)
711
                break;
712
713
            predicateEnd = inputString.indexOf(PREDICATE_END, predicateStart);
714
715
            if (predicateEnd == -1)
716
            {
717 5311 daigle
                logMetacat.warn("QuerySpecification.handleReturnField - Invalid path: " + inputString);
718 3235 sledge
                return;
719
            }
720
721
            while (attributePos < predicateEnd)
722
            {
723
                attributePos = inputString.indexOf(ATTRIBUTESYMBOL, attributePos + 1);
724
725
                if (attributePos == -1)
726
                    break;
727
            }
728
        }
729
730
        if (hasPredicate)
731 3769 tao
        {
732 3235 sledge
            containsPredicates = true;
733 3769 tao
            returnFieldListWithPredicates.add(inputString);
734
        }
735 3235 sledge
736
        containsExtendedSQL = true;
737 5311 daigle
738 3769 tao
        // no attribute value will be returned
739 5311 daigle
        logMetacat.info("QuerySpecification.handleReturnField - there are no attributes in the XPATH statement" );
740
        returnFieldList.add(inputString);
741 3235 sledge
    }
742
743 2067 jones
    /**
744
     * create a SQL serialization of the query that this instance represents
745
     */
746 6602 leinfelder
    public String printSQL(boolean useXMLIndex, List<Object> parameterValues)
747 2067 jones
    {
748
749
        StringBuffer self = new StringBuffer();
750 2366 sgarg
        StringBuffer queryString = new StringBuffer();
751 2067 jones
752 7417 leinfelder
        queryString.append("SELECT xml_documents.docid, identifier.guid, docname, doctype, date_created, date_updated, xml_documents.rev ");
753 7407 leinfelder
        queryString.append("FROM xml_documents, identifier ");
754
        queryString.append("WHERE xml_documents.docid = identifier.docid AND xml_documents.rev = identifier.rev AND");
755 2067 jones
756 2366 sgarg
        // Get the query from the QueryGroup and check
757
        // if no query has been returned
758 5204 daigle
        String queryFromQueryGroup;
759 6602 leinfelder
        // keep track of the values we add as prepared statement question marks (?)
760
        List<Object> groupValues = new ArrayList<Object>();
761 5204 daigle
        if (query != null) {
762 6602 leinfelder
        	queryFromQueryGroup = query.printSQL(useXMLIndex, groupValues);
763 5204 daigle
        } else {
764
        	queryFromQueryGroup = "";
765
        }
766 5311 daigle
        logMetacat.info("QuerySpecification.printSQL - Query : " + queryFromQueryGroup);
767 2677 sgarg
768 2373 sgarg
        if(!queryFromQueryGroup.trim().equals("")){
769 7407 leinfelder
            self.append(" xml_documents.docid IN (");
770 2373 sgarg
            self.append(queryFromQueryGroup);
771 2366 sgarg
            self.append(") ");
772 6602 leinfelder
            // add the parameter values
773
            parameterValues.addAll(groupValues);
774 2366 sgarg
        }
775 2067 jones
776
        // Add SQL to filter for doctypes requested in the query
777
        // This is an implicit OR for the list of doctypes. Only doctypes in
778
        // this
779
        // list will be searched if the tag is present
780
        if (!filterDocList.isEmpty()) {
781
            boolean firstdoctype = true;
782 2366 sgarg
783 7495 leinfelder
            if (!self.toString().equals("")){
784
                self.append(" AND ");
785 2366 sgarg
            }
786 7495 leinfelder
            self.append(" (");
787 2366 sgarg
788 2067 jones
            Enumeration en = filterDocList.elements();
789
            while (en.hasMoreElements()) {
790
                String currentDoctype = (String) en.nextElement();
791
                if (firstdoctype) {
792
                    firstdoctype = false;
793 7495 leinfelder
                    self.append(" doctype = ?");
794 2067 jones
                } else {
795 7495 leinfelder
                    self.append(" OR doctype = ?");
796 2067 jones
                }
797 7495 leinfelder
                parameterValues.add(currentDoctype);
798
799 2067 jones
            }
800 2366 sgarg
801 7495 leinfelder
            self.append(") ");
802
803 535 jones
        }
804 2067 jones
805
        // Add SQL to filter for owners requested in the query
806
        // This is an implicit OR for the list of owners
807
        if (!ownerList.isEmpty()) {
808
            boolean first = true;
809 2366 sgarg
810 7495 leinfelder
            if (!self.toString().equals("")){
811
                self.append(" AND ");
812 2366 sgarg
            }
813 7495 leinfelder
            self.append(" (");
814
815 2366 sgarg
816 2067 jones
            Enumeration en = ownerList.elements();
817
            while (en.hasMoreElements()) {
818
                String current = (String) en.nextElement();
819
                if (current != null) {
820
                    current = current.toLowerCase();
821
                }
822
                if (first) {
823
                    first = false;
824 7495 leinfelder
                    self.append(" lower(user_owner) = ?");
825 2067 jones
                } else {
826 7495 leinfelder
                    self.append(" OR lower(user_owner) = ?");
827 2067 jones
                }
828 7495 leinfelder
                parameterValues.add(current);
829 2067 jones
            }
830 2366 sgarg
831 7495 leinfelder
            self.append(") ");
832
833 2067 jones
        }
834
835
        // if there is only one percentage search item, this query is a
836 5311 daigle
        // percentage search query
837
        if (query != null) {
838
        	logMetacat.info("QuerySpecification.printSQL - percentage number: " + query.getPercentageSymbolCount());
839
			if (query.getPercentageSymbolCount() == 1) {
840
				logMetacat.info("QuerySpecification.printSQL - It is a percentage search");
841
				percentageSearch = true;
842
			}
843 2067 jones
        }
844
845 2366 sgarg
        queryString.append(self.toString());
846
        return queryString.toString();
847 535 jones
    }
848 2067 jones
849 3355 tao
850 2067 jones
851
    /**
852
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
853 2069 jones
     * pathquery document. This allows for customization of the returned fields.
854 2093 tao
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
855
     * xml_nodes to find the fields to be included by their path expression, and
856 2069 jones
     * avoids the use of the xml_index table.
857 2093 tao
     *
858 2073 jones
     * @param doclist the list of document ids to search
859 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id) which
860 2073 jones
     *            this user should not access
861 2093 tao
     * @param useXMLIndex a boolean flag indicating whether to search using
862 2073 jones
     *            xml_index
863 2069 jones
     */
864 6734 leinfelder
    public String printExtendedSQL(String doclist, boolean useXMLIndex, List<Object> allValues, List<Object> docListValues)
865 2069 jones
    {
866 6602 leinfelder
867
    	// keep track of the values we add as prepared statement question marks (?)
868
    	//List<Object> allValues = new ArrayList<Object>();
869
870
        if (useXMLIndex && !containsPredicates) {
871
        	// keep track of the values we add as prepared statement question marks (?)
872
        	List<Object> parameterValues = new ArrayList<Object>();
873 6734 leinfelder
        	String query = printExtendedSQL(doclist, parameterValues, docListValues);
874 6602 leinfelder
        	// add parameter values to our running list
875
        	allValues.addAll(parameterValues);
876
        	return query;
877 3235 sledge
        }
878
        else
879
        {
880 2069 jones
            StringBuffer self = new StringBuffer();
881
            boolean firstfield = true;
882 6602 leinfelder
            // keep track of the values we add as prepared statement question marks (?)
883
        	List<Object> parameterValues = new ArrayList<Object>();
884 3769 tao
            // first part comes from fields without  predicates
885 6734 leinfelder
            String queryFromWithoutPrecidates = printExtendedSQL(doclist, parameterValues, docListValues);
886 6602 leinfelder
            // add parameter values to our running list
887
        	allValues.addAll(parameterValues);
888
        	if (queryFromWithoutPrecidates != null) {
889 3769 tao
            	 // it has return fields without predicate
890
            	 self.append(queryFromWithoutPrecidates);
891
            	 firstfield = false;
892 6602 leinfelder
        	}
893 2069 jones
            //put the returnfields into the query
894
            //the for loop allows for multiple fields
895 3769 tao
            for (int i = 0; i <   returnFieldListWithPredicates.size(); i++)
896 3235 sledge
            {
897
                if (firstfield)
898
                {
899 2069 jones
                    firstfield = false;
900 3235 sledge
                }
901
                else
902
                {
903 2093 tao
                    self.append(" UNION ");
904 2069 jones
                }
905 3769 tao
                String path  = (String)  returnFieldListWithPredicates.elementAt(i);
906 6734 leinfelder
                //path = path.replaceAll("'", "''");
907 6602 leinfelder
                // TODO: can we use prepared statements for this?
908
                allValues.add(path);
909 2069 jones
                self.append("select xml_nodes.docid, ");
910 6602 leinfelder
                self.append("? as path, ");
911 3634 leinfelder
                self.append("xml_nodes.nodedata, ");
912
                self.append("xml_nodes.parentnodeid, ");
913
                self.append("xml_nodes.nodetype ");
914 3771 tao
                //self.append("from xml_nodes, xml_documents ");
915
                self.append("from xml_nodes ");
916
                self.append("where ");
917 6602 leinfelder
                // keep track of the values we add as prepared statement question marks (?)
918
            	List<Object> nestedParameterValues = new ArrayList<Object>();
919
                String nestedQuery = QueryTerm.useNestedStatements(path, nestedParameterValues);
920
                self.append(nestedQuery);
921
                // add to the running total
922
                allValues.addAll(nestedParameterValues);
923 2093 tao
924 2069 jones
                self.append(" AND xml_nodes.docid in (");
925
                self.append(doclist);
926 6734 leinfelder
                allValues.addAll(docListValues);
927
928 3771 tao
                if (returnFieldIsAttribute(path))
929
                {
930
                    self.append(")");
931
                }
932
                else
933
                {
934
                     self.append(") AND xml_nodes.nodetype = 'TEXT'");
935
                }
936
                //self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
937 2093 tao
938 3248 tao
                //addAccessRestrictionSQL(unaccessableNodePair, self);
939 2069 jones
            }
940
941
            return self.toString();
942
        }
943
    }
944 3771 tao
945
    /*
946
     * Determines the returnfield is an attribute of not.
947
     * For given returnfield, this programm will cut the part of path after last slash.
948
     * If no slash in the path, the original string will be considered as last part.
949
     * If first character of last part is @ it will retrun true.
950
     */
951
    private boolean returnFieldIsAttribute(String path)
952
    {
953
    	boolean isAttribute = false;
954
    	if (path != null)
955
    	{
956
    	    int slashIndex = path.lastIndexOf("/");
957
    	    if (slashIndex !=-1)
958
    	    {
959
    	    	// if there is slash in the path, path should be replace by the last part
960
    	    	path = path.substring(slashIndex+1);
961
    	    }
962 5311 daigle
    	    logMetacat.debug("QuerySpecification.returnFieldIsAttribute - final path is " + path);
963 3771 tao
    	    // if first of character of path is @, the path is attribute
964
    	    if (path.charAt(0) == '@')
965
    	    {
966 5311 daigle
    	    	logMetacat.debug("QuerySpecification.returnFieldIsAttribute - it is an attribute");
967 3771 tao
    	    	isAttribute = true;
968
    	    }
969
    	}
970
    	return isAttribute;
971
    }
972 2093 tao
973 2069 jones
    /**
974
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
975
     * pathquery document. This allows for customization of the returned fields.
976
     * It uses the xml_index table and so assumes that this table has been
977
     * built.
978 2093 tao
     *
979 2073 jones
     * @param doclist the list of document ids to search
980 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id)
981 2073 jones
     *            which this user should not access
982 2067 jones
     */
983 6734 leinfelder
    private String printExtendedSQL(String doclist, List<Object> values, List<Object> docListValues) {
984 6602 leinfelder
985
    	// keep track of the values we add as prepared statement question marks (?)
986
    	//List<Object> values = new ArrayList<Object>();
987
988 5311 daigle
        logMetacat.debug("QuerySpecification.printExtendedSQL - in printExtendedSQL");
989 2067 jones
        StringBuffer self = new StringBuffer();
990 6602 leinfelder
        Vector<String> elementVector = new Vector<String>();
991
        Vector<String> attributeVector = new Vector<String>();
992 2472 cjones
993 2523 sgarg
        boolean usePathIndex = true;
994 2434 sgarg
995 2523 sgarg
        // test if the are elements in the return fields
996
        if ( returnFieldList.size() == 0 ) {
997
            return null;
998
        }
999 2067 jones
1000 2523 sgarg
        for (int i = 0; i < returnFieldList.size(); i++) {
1001 3355 tao
        	String path = (String)returnFieldList.elementAt(i);
1002 3769 tao
        	// Since return fileds having preicates will be handle in another path,
1003
        	// we should skip it.
1004 4854 daigle
        	if (returnFieldListWithPredicates.contains(path)) {
1005 3769 tao
        		continue;
1006
        	}
1007 4854 daigle
1008
        	if (path != null && path.indexOf(ATTRIBUTESYMBOL) != -1) {
1009 3355 tao
        		attributeVector.add(path);
1010 4854 daigle
        	} else {
1011 3355 tao
        		elementVector.add(path);
1012 4812 daigle
        	}
1013 4854 daigle
1014
1015 4812 daigle
        	try {
1016 4854 daigle
				if (!SystemUtil.getPathsForIndexing().contains(path)) {
1017
					usePathIndex = false;
1018
				}
1019
			} catch (MetacatUtilException mue) {
1020 5311 daigle
				logMetacat.warn("QuerySpecification.printExtendedSQL - Could not get index paths: "  + mue.getMessage());
1021 4854 daigle
			}
1022 3355 tao
1023 2523 sgarg
        }
1024 3355 tao
        // check if has return field
1025
        if (elementVector.size() == 0 && attributeVector.size()==0)
1026
        {
1027
        	return null;
1028
        }
1029 2073 jones
1030 6602 leinfelder
        if (usePathIndex){
1031 3646 leinfelder
            self.append("select docid, path, nodedata, parentnodeid, null as nodetype ");
1032 6602 leinfelder
            self.append("from xml_path_index where path in ( ");
1033 2523 sgarg
1034
            boolean firstfield = true;
1035
            //put the returnfields into the query
1036
            //the for loop allows for multiple fields
1037
            for (int i = 0; i < returnFieldList.size(); i++) {
1038 6146 leinfelder
            	String returnField = (String) returnFieldList.elementAt(i);
1039
            	// in case we have predicate conditions with quotes
1040
            	returnField = returnField.replaceAll("'", "''");
1041 2523 sgarg
                if (firstfield) {
1042
                    firstfield = false;
1043 6602 leinfelder
                    self.append("? ");
1044
                	values.add(returnField);
1045 2523 sgarg
                }
1046
                else {
1047 6602 leinfelder
                    self.append(", ? ");
1048
                    values.add(returnField);
1049 2523 sgarg
                }
1050
            }
1051
            self.append(") AND docid in (");
1052
            self.append(doclist);
1053 6734 leinfelder
            values.addAll(docListValues);
1054 2523 sgarg
            self.append(")");
1055
1056
        } else {
1057
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1058 3634 leinfelder
            self.append("xml_nodes.parentnodeid, ");
1059
            self.append("xml_nodes.nodetype ");
1060 3355 tao
            self.append("FROM xml_index, xml_nodes WHERE (");
1061
1062
            boolean firstElement = true;
1063
            boolean firstAttribute = true;
1064 2523 sgarg
            //put the returnfields into the query
1065
            //the for loop allows for multiple fields
1066 3355 tao
            if (elementVector.size() != 0)
1067
            {
1068
	            for (int i = 0; i < elementVector.size(); i++) {
1069
	            	String path = (String) elementVector.elementAt(i);
1070
	                if (firstElement) {
1071
	                	firstElement = false;
1072 6602 leinfelder
	                	self.append(" (xml_index.nodeid=xml_nodes.parentnodeid AND xml_index.path IN ( ");
1073
	                    self.append("?");
1074
	                    values.add(path);
1075 3355 tao
	                 }
1076
	                else
1077
	                {
1078 6602 leinfelder
	                    self.append(", ? ");
1079
	                    values.add(path);
1080 3355 tao
	                }
1081
	            }
1082
	            self.append(") AND xml_nodes.nodetype = 'TEXT')");
1083 2523 sgarg
            }
1084 3355 tao
1085
            if (attributeVector.size() != 0)
1086
            {
1087
            	for (int j=0; j<attributeVector.size(); j++)
1088
            	{
1089
            		String path = (String) attributeVector.elementAt(j);
1090
            		if (firstAttribute)
1091
            		{
1092
            			firstAttribute = false;
1093
            			if (!firstElement)
1094
                		{
1095
                			self.append(" OR ");
1096
                		}
1097 6602 leinfelder
            			self.append(" (xml_index.nodeid=xml_nodes.nodeid AND ( xml_index.path IN ( ");
1098
	                    self.append("?");
1099
	                    values.add(path);
1100 3355 tao
            		}
1101
            		else
1102
	                {
1103 6602 leinfelder
	                    self.append(", ? ");
1104
	                    values.add(path);
1105 3355 tao
	                }
1106
            	}
1107
            	self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'))");
1108
            }
1109
1110
1111 2523 sgarg
            self.append(") AND xml_nodes.docid in (");
1112
            self.append(doclist);
1113 6734 leinfelder
            values.addAll(docListValues);
1114 3355 tao
            self.append(")");
1115 2523 sgarg
1116
        }
1117
1118
        return self.toString();
1119 2073 jones
    }
1120
1121 2419 sgarg
1122 2073 jones
    /**
1123 2419 sgarg
     * Method to return a String generated after sorting the returnFieldList
1124
     * Vector
1125
     */
1126
    public String getSortedReturnFieldString(){
1127
        String returnFields = "";
1128
1129
        // Create a temporary vector and copy returnFieldList into it
1130
        Vector tempVector = new Vector();
1131 2464 sgarg
1132 2419 sgarg
        Iterator it = returnFieldList.iterator();
1133
        while(it.hasNext()){
1134
            tempVector.add(it.next());
1135
        }
1136
1137 3308 tao
        /*Enumeration attEnum = attributeReturnList.elements();
1138 2464 sgarg
        while(attEnum.hasMoreElements()){
1139
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1140
	    String rfield = "";
1141
            if(tempIt.hasNext()){
1142
		String element = (String)tempIt.next();
1143 2474 sgarg
		if(element != null) {
1144
		    rfield +=element;
1145 2464 sgarg
		}
1146
	    }
1147
            if(tempIt.hasNext()){
1148
		String attribute = (String)tempIt.next();
1149 2474 sgarg
		if(attribute != null) {
1150
  		    rfield = rfield + "@" + attribute;
1151 2464 sgarg
                }
1152
	    }
1153
            tempVector.add(rfield);
1154 3308 tao
        }*/
1155 2464 sgarg
1156 2419 sgarg
        // Sort the temporary vector
1157
        java.util.Collections.sort(tempVector);
1158
1159
        // Generate the string and return it
1160
        it = tempVector.iterator();
1161
        while(it.hasNext()){
1162
            returnFields = returnFields + it.next() + "|";
1163
        }
1164
        return returnFields;
1165
    }
1166
1167
1168 3355 tao
1169 2067 jones
1170 2074 jones
1171 2067 jones
    public static String printRelationSQL(String docid)
1172 1354 tao
    {
1173 2067 jones
        StringBuffer self = new StringBuffer();
1174
        self.append("select subject, relationship, object, subdoctype, ");
1175
        self.append("objdoctype from xml_relation ");
1176
        self.append("where docid like '").append(docid).append("'");
1177
        return self.toString();
1178 1354 tao
    }
1179 2066 jones
1180 2067 jones
    public static String printGetDocByDoctypeSQL(String docid)
1181
    {
1182
        StringBuffer self = new StringBuffer();
1183 465 berkley
1184 2067 jones
        self.append("SELECT docid,docname,doctype,");
1185
        self.append("date_created, date_updated ");
1186
        self.append("FROM xml_documents WHERE docid IN (");
1187
        self.append(docid).append(")");
1188
        return self.toString();
1189
    }
1190 159 jones
1191 2067 jones
    /**
1192
     * create a String description of the query that this instance represents.
1193
     * This should become a way to get the XML serialization of the query.
1194
     */
1195
    public String toString()
1196
    {
1197
        return "meta_file_id=" + meta_file_id + "\n" + query;
1198
        //DOCTITLE attr cleared from the db
1199
        //return "meta_file_id=" + meta_file_id + "\n" +
1200
        //"querytitle=" + querytitle + "\n" + query;
1201
    }
1202
1203 2073 jones
    /** A method to get rid of attribute part in path expression */
1204 2067 jones
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1205
    {
1206
        if (pathExpression == null) { return null; }
1207
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1208
        String newExpression = null;
1209 2458 cjones
        if (index != 0) {
1210 2067 jones
            newExpression = pathExpression.substring(0, index - 1);
1211
        }
1212 5311 daigle
        logMetacat.info("QuerySpecification.newPathExpressionWithOutAttribute - The path expression without attributes: "
1213 2663 sgarg
                + newExpression);
1214 2067 jones
        return newExpression;
1215
    }
1216
1217 2073 jones
    /** A method to get attribute name from path */
1218 2067 jones
    public static String getAttributeName(String path)
1219
    {
1220
        if (path == null) { return null; }
1221
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1222
        int size = path.length();
1223
        String attributeName = null;
1224
        if (index != 1) {
1225
            attributeName = path.substring(index + 1, size);
1226
        }
1227 5311 daigle
        logMetacat.info("QuerySpecification.getAttributeName - The attirbute name from path: " + attributeName);
1228 2067 jones
        return attributeName;
1229
    }
1230
1231 155 jones
}