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
import java.util.Enumeration;
36 155 jones
import java.util.Stack;
37 158 jones
import java.util.Vector;
38 155 jones
39 4080 daigle
import edu.ucsb.nceas.metacat.util.MetaCatUtil;
40 2067 jones
41 2663 sgarg
import org.apache.log4j.Logger;
42 185 jones
import org.xml.sax.Attributes;
43 158 jones
import org.xml.sax.InputSource;
44
import org.xml.sax.SAXException;
45 185 jones
import org.xml.sax.XMLReader;
46 2067 jones
import org.xml.sax.helpers.DefaultHandler;
47 185 jones
import org.xml.sax.helpers.XMLReaderFactory;
48 2419 sgarg
import java.util.Iterator;
49 155 jones
50 402 berkley
/**
51 2067 jones
 * A Class that represents a structured query, and can be constructed from an
52
 * XML serialization conforming to
53 2093 tao
 *
54 2067 jones
 * @see pathquery.dtd. The printSQL() method can be used to print a SQL
55
 *      serialization of the query.
56 155 jones
 */
57 2067 jones
public class QuerySpecification extends DefaultHandler
58
{
59 1832 tao
60 2067 jones
    /** flag determining whether extended query terms are present */
61
    private boolean containsExtendedSQL = false;
62 158 jones
63 3235 sledge
    /** flag determining whether predicates are present */
64
    private boolean containsPredicates = false;
65
66 2067 jones
    /** Identifier for this query document */
67
    private String meta_file_id;
68 158 jones
69 2067 jones
    /** Title of this query */
70
    private String queryTitle;
71
72
    /** List of document types to be returned using package back tracing */
73
    private Vector returnDocList;
74
75
    /** List of document types to be searched */
76
    private Vector filterDocList;
77
78
    /** List of fields to be returned in result set */
79
    private Vector returnFieldList;
80 3769 tao
81
    /** List of fields with "[" and "]" in result set. This is a subset of returnFieldList.
82
     *   If some of return fields have [,  those fields will be stored this vector (we have different query for those return fields */
83
    private Vector returnFieldListWithPredicates;
84 2067 jones
85
    /** List of users owning documents to be searched */
86
    private Vector ownerList;
87
88
    /** The root query group that contains the recursive query constraints */
89
    private QueryGroup query = null;
90 3766 tao
91
    /** A string buffer to stored normalized query (Sometimes, the query have
92
     * a value like "&", it will cause problem in html transform). So we need a
93
     * normalized query xml string.
94
     */
95
    private StringBuffer xml = new StringBuffer();
96 2067 jones
97
    // Query data structures used temporarily during XML parsing
98
    private Stack elementStack;
99
100
    private Stack queryStack;
101
102
    private String currentValue;
103
104
    private String currentPathexpr;
105
106
    private String parserName = null;
107
108
    private String accNumberSeparator = null;
109
110
    private boolean percentageSearch = false;
111
112
    private String userName = null;
113
114
    private static final String PUBLIC = "public";
115
116
    private String[] group = null;
117
118
    public static final String ATTRIBUTESYMBOL = "@";
119
120 3235 sledge
    public static final char PREDICATE_START = '[';
121
122
    public static final char PREDICATE_END = ']';
123
124 3308 tao
    //private boolean hasAttributeReturnField = false;
125 2067 jones
126 3308 tao
    //private Hashtable attributeReturnList = new Hashtable();
127 2067 jones
128 3308 tao
    //private int countAttributeReturnField = 0;
129 2067 jones
130 2093 tao
    private StringBuffer textBuffer = new StringBuffer();
131 3223 tao
132
133 2663 sgarg
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
134
135 2067 jones
    /**
136
     * construct an instance of the QuerySpecification class
137 2093 tao
     *
138 2067 jones
     * @param queryspec
139
     *            the XML representation of the query (should conform to
140
     *            pathquery.dtd) as a Reader
141
     * @param parserName
142
     *            the fully qualified name of a Java Class implementing the
143
     *            org.xml.sax.XMLReader interface
144
     */
145
    public QuerySpecification(Reader queryspec, String parserName,
146
            String accNumberSeparator) throws IOException
147
    {
148
        super();
149
150
        // Initialize the class variables
151
        returnDocList = new Vector();
152
        filterDocList = new Vector();
153
        elementStack = new Stack();
154
        queryStack = new Stack();
155
        returnFieldList = new Vector();
156 3769 tao
        returnFieldListWithPredicates = new Vector();
157 2067 jones
        ownerList = new Vector();
158
        this.parserName = parserName;
159
        this.accNumberSeparator = accNumberSeparator;
160
161
        // Initialize the parser and read the queryspec
162
        XMLReader parser = initializeParser();
163
        if (parser == null) {
164
            System.err.println("SAX parser not instantiated properly.");
165
        }
166
        try {
167
            parser.parse(new InputSource(queryspec));
168
        } catch (SAXException e) {
169
            System.err.println("error parsing data in "
170
                    + "QuerySpecification.QuerySpecification");
171
            System.err.println(e.getMessage());
172
        }
173 181 jones
    }
174 2067 jones
175
    /**
176
     * construct an instance of the QuerySpecification class
177 2093 tao
     *
178 2067 jones
     * @param queryspec
179
     *            the XML representation of the query (should conform to
180
     *            pathquery.dtd) as a String
181
     * @param parserName
182
     *            the fully qualified name of a Java Class implementing the
183
     *            org.xml.sax.Parser interface
184
     */
185
    public QuerySpecification(String queryspec, String parserName,
186
            String accNumberSeparator) throws IOException
187
    {
188
        this(new StringReader(queryspec), parserName, accNumberSeparator);
189 155 jones
    }
190
191 2067 jones
    /**
192
     * construct an instance of the QuerySpecification class which don't need
193
     * to parser a xml document
194 2093 tao
     *
195 2067 jones
     * @param accNumberSeparator
196
     *            the separator between doc version
197
     */
198
    public QuerySpecification(String accNumberSeparator) throws IOException
199 2045 tao
    {
200 2067 jones
        // Initialize the class variables
201
        returnDocList = new Vector();
202
        filterDocList = new Vector();
203
        elementStack = new Stack();
204
        queryStack = new Stack();
205
        returnFieldList = new Vector();
206 3769 tao
        returnFieldListWithPredicates = new Vector();
207 2067 jones
        ownerList = new Vector();
208
        this.accNumberSeparator = accNumberSeparator;
209 2045 tao
    }
210 2067 jones
211
    /**
212
     * Method to set user name
213 2093 tao
     *
214 2067 jones
     * @param myName
215
     *            the user name
216
     */
217
    public void setUserName(String myName)
218 2045 tao
    {
219 2067 jones
        //to lower case
220
        if (myName != null) {
221
            this.userName = myName.toLowerCase();
222
        } else {
223
            this.userName = myName;
224
        }
225 2045 tao
    }
226 2067 jones
227
    /**
228
     * Method to set user group
229 2093 tao
     *
230 2067 jones
     * @param myGroup
231
     *            the user group
232
     */
233
    public void setGroup(String[] myGroup)
234 1301 tao
    {
235 2067 jones
        this.group = myGroup;
236 1301 tao
    }
237 2067 jones
238
    /**
239
     * Method to indicate this query is a percentage search
240
     */
241
    public boolean isPercentageSearch()
242 1301 tao
    {
243 2067 jones
        return percentageSearch;
244 1301 tao
    }
245 2067 jones
246
    /*
247
     * Method to get owner query. If it is owner it has all permission
248
     */
249
    private String createOwerQuery()
250 1301 tao
    {
251 2067 jones
        String ownerQuery = null;
252 3223 tao
        //if user is public, we don't need to run owner query
253
        if (userName != null && !userName.equalsIgnoreCase(PUBLIC))
254
        {
255
	        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
256
	        if (userName != null && !userName.equals("")) {
257
	            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
258
	        }
259 2067 jones
        }
260 2663 sgarg
        logMetacat.info("OwnerQuery: " + ownerQuery);
261 2067 jones
        return ownerQuery;
262
    }
263
264
    /*
265
     * Method to create query for xml_access, this part is to get docid list
266
     * which have a allow rule for a given user
267
     */
268
    private String createAllowRuleQuery()
269 1301 tao
    {
270 2067 jones
        String allowQuery = null;
271
        String allowString = constructAllowString();
272
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
273 3312 tao
        allowQuery = allowQuery + ")";
274 2663 sgarg
        logMetacat.info("allow query is: " + allowQuery);
275 2067 jones
        return allowQuery;
276
277 1301 tao
    }
278 2067 jones
279
    /* Method to construct a allow rule string */
280
    private String constructAllowString()
281 1301 tao
    {
282 2067 jones
        String allowQuery = "";
283 3313 tao
284
       // add public
285
        allowQuery = "(lower(principal_name) = '" + PUBLIC
286
                + "'";
287
288
        // add user name
289
        if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
290
            allowQuery = allowQuery + "OR lower(principal_name) = '" + userName +"'";
291
292 2067 jones
        }
293 3313 tao
        // add  group
294 2067 jones
        if (group != null) {
295
            for (int i = 0; i < group.length; i++) {
296
                String groupUint = group[i];
297
                if (groupUint != null && !groupUint.equals("")) {
298
                    groupUint = groupUint.toLowerCase();
299 3313 tao
                    allowQuery = allowQuery + " OR lower(principal_name) = '"
300
                            + groupUint + "'";
301 2067 jones
                }//if
302
            }//for
303 1301 tao
        }//if
304 3313 tao
        // add allow rule
305
        allowQuery = allowQuery + ") AND perm_type = 'allow'" + " AND permission > 3";
306 2663 sgarg
        logMetacat.info("allow string is: " + allowQuery);
307 2067 jones
        return allowQuery;
308
    }
309 155 jones
310 2067 jones
    /*
311
     * Method to create query for xml_access, this part is to get docid list
312
     * which have a deny rule and perm_order is allowFirst for a given user.
313
     * This means the user will be denied to read
314
     */
315
    private String createDenyRuleQuery()
316
    {
317
        String denyQuery = null;
318
        String denyString = constructDenyString();
319
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
320 3312 tao
        denyQuery = denyQuery + ") ";
321 2663 sgarg
        logMetacat.info("denyquery is: " + denyQuery);
322 2067 jones
        return denyQuery;
323 711 jones
324 2067 jones
    }
325 181 jones
326 2067 jones
    /* Construct deny string */
327
    private String constructDenyString()
328 402 berkley
    {
329 2067 jones
        String denyQuery = "";
330 3313 tao
331
        // add public
332
        denyQuery = "(lower(principal_name) = '" + PUBLIC
333
                 + "'";
334
335
         // add user name
336
         if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
337
        	 denyQuery = denyQuery + "OR lower(principal_name) = '" + userName +"'";
338
339
         }
340
         // add  groups
341
         if (group != null) {
342
             for (int i = 0; i < group.length; i++) {
343
                 String groupUint = group[i];
344
                 if (groupUint != null && !groupUint.equals("")) {
345
                     groupUint = groupUint.toLowerCase();
346
                     denyQuery = denyQuery + " OR lower(principal_name) = '"
347
                             + groupUint + "'";
348
                 }//if
349
             }//for
350
         }//if
351
         // add deny rules
352
         denyQuery = denyQuery + ") AND perm_type = 'deny'" +  " AND perm_order ='allowFirst'" +" AND permission > 3";
353
         logMetacat.info("allow string is: " + denyQuery);
354
         return denyQuery;
355
356 402 berkley
    }
357 2067 jones
358
    /**
359
     * Method to append a access control query to SQL. So in DBQuery class, we
360
     * can get docid from both user specified query and access control query.
361
     * We don't need to checking permission after we get the doclist. It will
362
     * be good to performance
363 2093 tao
     *
364 2067 jones
     */
365
    public String getAccessQuery()
366 402 berkley
    {
367 2067 jones
        String accessQuery = null;
368
        String onwer = createOwerQuery();
369
        String allow = createAllowRuleQuery();
370
        String deny = createDenyRuleQuery();
371 3313 tao
        //logMetacat.warn("onwer " +onwer);
372
        //logMetacat.warn("allow "+allow);
373
        //logMetacat.warn("deny "+deny);
374 3223 tao
        if (onwer != null)
375
        {
376
          accessQuery = " AND (docid IN(" + onwer + ")";
377
          accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
378 2067 jones
                + " AND docid NOT IN (" + deny + ")))";
379 3223 tao
        }
380
        else
381
        {
382
        	accessQuery = " AND (docid IN (" + allow + ")"
383
                + " AND docid NOT IN (" + deny + "))";
384
        }
385 3313 tao
        logMetacat.warn("accessquery is: " + accessQuery);
386 2067 jones
        return accessQuery;
387 402 berkley
    }
388 745 jones
389 2067 jones
    /**
390
     * Returns true if the parsed query contains and extended xml query (i.e.
391
     * there is at least one &lt;returnfield&gt; in the pathquery document)
392
     */
393
    public boolean containsExtendedSQL()
394
    {
395
        if (containsExtendedSQL) {
396
            return true;
397
        } else {
398
            return false;
399
        }
400
    }
401 745 jones
402 3308 tao
403 2067 jones
    /**
404
     * Accessor method to return the identifier of this Query
405
     */
406
    public String getIdentifier()
407
    {
408
        return meta_file_id;
409
    }
410 155 jones
411 2067 jones
    /**
412
     * method to set the identifier of this query
413
     */
414
    public void setIdentifier(String id)
415
    {
416
        this.meta_file_id = id;
417
    }
418 745 jones
419 2067 jones
    /**
420
     * Accessor method to return the title of this Query
421
     */
422
    public String getQueryTitle()
423
    {
424
        return queryTitle;
425
    }
426 745 jones
427 2067 jones
    /**
428
     * method to set the title of this query
429
     */
430
    public void setQueryTitle(String title)
431
    {
432
        this.queryTitle = title;
433
    }
434 745 jones
435 2067 jones
    /**
436
     * Accessor method to return a vector of the return document types as
437
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
438
     */
439
    public Vector getReturnDocList()
440
    {
441
        return this.returnDocList;
442
    }
443 745 jones
444 2067 jones
    /**
445
     * method to set the list of return docs of this query
446
     */
447
    public void setReturnDocList(Vector returnDocList)
448
    {
449
        this.returnDocList = returnDocList;
450
    }
451 745 jones
452 2067 jones
    /**
453
     * Accessor method to return a vector of the filter doc types as defined in
454
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
455
     */
456
    public Vector getFilterDocList()
457
    {
458
        return this.filterDocList;
459
    }
460 172 jones
461 2067 jones
    /**
462
     * method to set the list of filter docs of this query
463
     */
464
    public void setFilterDocList(Vector filterDocList)
465
    {
466
        this.filterDocList = filterDocList;
467
    }
468 155 jones
469 2067 jones
    /**
470
     * Accessor method to return a vector of the extended return fields as
471
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
472
     */
473
    public Vector getReturnFieldList()
474
    {
475
        return this.returnFieldList;
476
    }
477 155 jones
478 2067 jones
    /**
479
     * method to set the list of fields to be returned by this query
480
     */
481
    public void setReturnFieldList(Vector returnFieldList)
482
    {
483
        this.returnFieldList = returnFieldList;
484
    }
485 155 jones
486 2067 jones
    /**
487
     * Accessor method to return a vector of the owner fields as defined in the
488
     * &lt;owner&gt; tag in the pathquery dtd.
489
     */
490
    public Vector getOwnerList()
491
    {
492
        return this.ownerList;
493
    }
494 155 jones
495 2067 jones
    /**
496
     * method to set the list of owners used to constrain this query
497
     */
498
    public void setOwnerList(Vector ownerList)
499
    {
500
        this.ownerList = ownerList;
501 155 jones
    }
502
503 2067 jones
    /**
504
     * get the QueryGroup used to express query constraints
505
     */
506
    public QueryGroup getQueryGroup()
507
    {
508
        return query;
509 158 jones
    }
510 155 jones
511 2067 jones
    /**
512
     * set the querygroup
513
     */
514
    public void setQueryGroup(QueryGroup group)
515
    {
516
        query = group;
517 158 jones
    }
518
519 2067 jones
    /**
520
     * set if this query sepcification has extendQuery(has return doc type or
521
     * not)
522
     */
523
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
524
    {
525
        containsExtendedSQL = hasExtenedQuery;
526
    }
527 158 jones
528 2067 jones
    /**
529
     * Set up the SAX parser for reading the XML serialized query
530
     */
531
    private XMLReader initializeParser()
532
    {
533
        XMLReader parser = null;
534
535
        // Set up the SAX document handlers for parsing
536
        try {
537
538
            // Get an instance of the parser
539
            parser = XMLReaderFactory.createXMLReader(parserName);
540
541
            // Set the ContentHandler to this instance
542
            parser.setContentHandler(this);
543
544
            // Set the error Handler to this instance
545
            parser.setErrorHandler(this);
546
547
        } catch (Exception e) {
548
            System.err.println("Error in QuerySpcecification.initializeParser "
549
                    + e.toString());
550
        }
551
552
        return parser;
553 1833 tao
    }
554 170 jones
555 2067 jones
    /**
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 3766 tao
        logMetacat.debug("start at startElement "+localName);
564 2067 jones
        BasicNode currentNode = new BasicNode(localName);
565 3766 tao
        //write element name into xml buffer.
566
        xml.append("<");
567
        xml.append(localName);
568 2067 jones
        // 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 3766 tao
                xml.append(" ");
575
                xml.append(atts.getLocalName(i));
576
                xml.append("=\"");
577
                xml.append(atts.getValue(i));
578
                xml.append("\"");
579 2067 jones
            }
580
        }
581 3766 tao
        xml.append(">");
582 170 jones
583 2067 jones
        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 3766 tao
        logMetacat.debug("end in startElement "+localName);
596 2067 jones
    }
597 172 jones
598 2067 jones
    /**
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 3766 tao
    	 logMetacat.debug("start in endElement "+localName);
607 2067 jones
        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 2093 tao
        } 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 3766 tao
              currentValue = MetaCatUtil.normalize(currentValue);
633 2093 tao
        } 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 172 jones
        }
646 3766 tao
        String normalizedXML = textBuffer.toString().trim();
647
        logMetacat.debug("================before normailze "+normalizedXML);
648
        normalizedXML =  MetaCatUtil.normalize(normalizedXML);
649
        logMetacat.debug("================after normailze "+normalizedXML);
650
        xml.append(normalizedXML);
651
        xml.append("</");
652
        xml.append(localName);
653
        xml.append(">");
654 2093 tao
        //rest textBuffer
655
        textBuffer = new StringBuffer();
656
657 172 jones
    }
658 3766 tao
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 743 jones
670 2067 jones
    /**
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 2093 tao
      // buffer all text nodes for same element. This is for text was splited
678
      // into different nodes
679 3766 tao
      String text = new String(ch, start, length);
680
      logMetacat.debug("the text in characters "+text);
681
      textBuffer.append(text);
682 2067 jones
683
    }
684
685 3358 tao
   /**
686
    * Method to handle return field. It will be callied in ecogrid part
687
    * @param inputString
688
    */
689
    public void handleReturnField(String inputString)
690 3235 sledge
    {
691
        int attributePos = inputString.indexOf(ATTRIBUTESYMBOL);
692
        int predicateStart = -1;
693
        int predicateEnd;
694
        boolean hasPredicate = false;
695 535 jones
696 3235 sledge
        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("handleReturnField(): ");
716
                logMetacat.warn("    Invalid path: " + inputString);
717
                return;
718
            }
719
720
            while (attributePos < predicateEnd)
721
            {
722
                attributePos = inputString.indexOf(ATTRIBUTESYMBOL, attributePos + 1);
723
724
                if (attributePos == -1)
725
                    break;
726
            }
727
        }
728
729
        if (hasPredicate)
730 3769 tao
        {
731 3235 sledge
            containsPredicates = true;
732 3769 tao
            returnFieldListWithPredicates.add(inputString);
733
        }
734 3235 sledge
735
        containsExtendedSQL = true;
736
737 3308 tao
738 3769 tao
        // no attribute value will be returned
739
        logMetacat.info("QuerySpecification.handleReturnField(): " );
740
        logMetacat.info("  there are no attributes in the XPATH statement" );
741
        returnFieldList.add(inputString);
742 3308 tao
743 3235 sledge
744 3308 tao
745 3235 sledge
    }
746
747 2067 jones
    /**
748
     * create a SQL serialization of the query that this instance represents
749
     */
750
    public String printSQL(boolean useXMLIndex)
751
    {
752
753
        StringBuffer self = new StringBuffer();
754 2366 sgarg
        StringBuffer queryString = new StringBuffer();
755 2067 jones
756 2366 sgarg
        queryString.append("SELECT docid,docname,doctype,");
757
        queryString.append("date_created, date_updated, rev ");
758
        queryString.append("FROM xml_documents WHERE");
759 2067 jones
760 2366 sgarg
        // Get the query from the QueryGroup and check
761
        // if no query has been returned
762
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
763 2677 sgarg
        logMetacat.info("Query from query in QuerySpec.printSQL: "
764
        		+ queryFromQueryGroup);
765
766 2373 sgarg
        if(!queryFromQueryGroup.trim().equals("")){
767 2366 sgarg
            self.append(" docid IN (");
768 2373 sgarg
            self.append(queryFromQueryGroup);
769 2366 sgarg
            self.append(") ");
770
        }
771 2067 jones
772
        // Add SQL to filter for doctypes requested in the query
773
        // This is an implicit OR for the list of doctypes. Only doctypes in
774
        // this
775
        // list will be searched if the tag is present
776
        if (!filterDocList.isEmpty()) {
777
            boolean firstdoctype = true;
778 2366 sgarg
            boolean emptyString = true;
779
780
            if(!self.toString().equals("")){
781
                self.append(" AND (");
782
                emptyString = false;
783
            }
784
785 2067 jones
            Enumeration en = filterDocList.elements();
786
            while (en.hasMoreElements()) {
787
                String currentDoctype = (String) en.nextElement();
788
                if (firstdoctype) {
789
                    firstdoctype = false;
790
                    self.append(" doctype = '" + currentDoctype + "'");
791
                } else {
792
                    self.append(" OR doctype = '" + currentDoctype + "'");
793
                }
794
            }
795 2366 sgarg
796
            if(!emptyString){
797
                self.append(") ");
798
            }
799 535 jones
        }
800 2067 jones
801
        // Add SQL to filter for owners requested in the query
802
        // This is an implicit OR for the list of owners
803
        if (!ownerList.isEmpty()) {
804
            boolean first = true;
805 2366 sgarg
            boolean emptyString = true;
806
807
            if(!self.toString().equals("")){
808
                self.append(" AND (");
809
                emptyString = false;
810
            }
811
812 2067 jones
            Enumeration en = ownerList.elements();
813
            while (en.hasMoreElements()) {
814
                String current = (String) en.nextElement();
815
                if (current != null) {
816
                    current = current.toLowerCase();
817
                }
818
                if (first) {
819
                    first = false;
820
                    self.append(" lower(user_owner) = '" + current + "'");
821
                } else {
822
                    self.append(" OR lower(user_owner) = '" + current + "'");
823
                }
824
            }
825 2366 sgarg
826
            if(!emptyString){
827
                self.append(") ");
828
            }
829 2067 jones
        }
830
831
        // if there is only one percentage search item, this query is a
832
        // percentage
833
        // search query
834 2663 sgarg
        logMetacat.info("percentage number: "
835
                + query.getPercentageSymbolCount());
836 2067 jones
        if (query.getPercentageSymbolCount() == 1) {
837 2663 sgarg
            logMetacat.info("It is a percentage search");
838 2067 jones
            percentageSearch = true;
839
        }
840
841 2366 sgarg
        queryString.append(self.toString());
842
        return queryString.toString();
843 535 jones
    }
844 2067 jones
845 3355 tao
846 2067 jones
847
    /**
848
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
849 2069 jones
     * pathquery document. This allows for customization of the returned fields.
850 2093 tao
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
851
     * xml_nodes to find the fields to be included by their path expression, and
852 2069 jones
     * avoids the use of the xml_index table.
853 2093 tao
     *
854 2073 jones
     * @param doclist the list of document ids to search
855 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id) which
856 2073 jones
     *            this user should not access
857 2093 tao
     * @param useXMLIndex a boolean flag indicating whether to search using
858 2073 jones
     *            xml_index
859 2069 jones
     */
860 3248 tao
    public String printExtendedSQL(String doclist, boolean useXMLIndex)
861 2069 jones
    {
862 3235 sledge
        if (useXMLIndex && !containsPredicates)
863
        {
864 3248 tao
            return printExtendedSQL(doclist);
865 3235 sledge
        }
866
        else
867
        {
868 2069 jones
            StringBuffer self = new StringBuffer();
869
            boolean firstfield = true;
870 3769 tao
            // first part comes from fields without  predicates
871
            String queryFromWithoutPrecidates = printExtendedSQL(doclist);
872
             if (queryFromWithoutPrecidates != null)
873
             {
874
            	 // it has return fields without predicate
875
            	 self.append(queryFromWithoutPrecidates);
876
            	 firstfield = false;
877
             }
878 2069 jones
            //put the returnfields into the query
879
            //the for loop allows for multiple fields
880 3769 tao
            for (int i = 0; i <   returnFieldListWithPredicates.size(); i++)
881 3235 sledge
            {
882
                if (firstfield)
883
                {
884 2069 jones
                    firstfield = false;
885 3235 sledge
                }
886
                else
887
                {
888 2093 tao
                    self.append(" UNION ");
889 2069 jones
                }
890 3769 tao
                String path  = (String)  returnFieldListWithPredicates.elementAt(i);
891 2069 jones
                self.append("select xml_nodes.docid, ");
892 3634 leinfelder
                self.append("'"+ path.replaceAll("'", "''") + "' as path, ");
893
                self.append("xml_nodes.nodedata, ");
894
                self.append("xml_nodes.parentnodeid, ");
895
                self.append("xml_nodes.nodetype ");
896 3771 tao
                //self.append("from xml_nodes, xml_documents ");
897
                self.append("from xml_nodes ");
898
                self.append("where ");
899 2069 jones
                self.append(QueryTerm.useNestedStatements(path));
900 2093 tao
901 2069 jones
                self.append(" AND xml_nodes.docid in (");
902
                self.append(doclist);
903 3771 tao
                if (returnFieldIsAttribute(path))
904
                {
905
                    self.append(")");
906
                }
907
                else
908
                {
909
                     self.append(") AND xml_nodes.nodetype = 'TEXT'");
910
                }
911
                //self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
912 2093 tao
913 3248 tao
                //addAccessRestrictionSQL(unaccessableNodePair, self);
914 2069 jones
            }
915
916
            return self.toString();
917
        }
918
    }
919 3771 tao
920
    /*
921
     * Determines the returnfield is an attribute of not.
922
     * For given returnfield, this programm will cut the part of path after last slash.
923
     * If no slash in the path, the original string will be considered as last part.
924
     * If first character of last part is @ it will retrun true.
925
     */
926
    private boolean returnFieldIsAttribute(String path)
927
    {
928
    	boolean isAttribute = false;
929
    	if (path != null)
930
    	{
931
    	    int slashIndex = path.lastIndexOf("/");
932
    	    if (slashIndex !=-1)
933
    	    {
934
    	    	// if there is slash in the path, path should be replace by the last part
935
    	    	path = path.substring(slashIndex+1);
936
    	    }
937
    	    logMetacat.debug("In QuerySpecification.returnFieldIsAttribute method, final path is "+path);
938
    	    // if first of character of path is @, the path is attribute
939
    	    if (path.charAt(0) == '@')
940
    	    {
941
    	    	logMetacat.debug("it is attribute");
942
    	    	isAttribute = true;
943
    	    }
944
    	}
945
    	return isAttribute;
946
    }
947 2093 tao
948 2069 jones
    /**
949
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
950
     * pathquery document. This allows for customization of the returned fields.
951
     * It uses the xml_index table and so assumes that this table has been
952
     * built.
953 2093 tao
     *
954 2073 jones
     * @param doclist the list of document ids to search
955 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id)
956 2073 jones
     *            which this user should not access
957 2067 jones
     */
958 3355 tao
    private String printExtendedSQL(String doclist)
959 402 berkley
    {
960 2663 sgarg
        logMetacat.info("querySpecification.printExtendedSQL called\n");
961 2067 jones
        StringBuffer self = new StringBuffer();
962 3355 tao
        Vector elementVector = new Vector();
963
        Vector attributeVector = new Vector();
964 2472 cjones
965 2523 sgarg
        boolean usePathIndex = true;
966 2434 sgarg
967 2523 sgarg
        // test if the are elements in the return fields
968
        if ( returnFieldList.size() == 0 ) {
969
            return null;
970
        }
971 2067 jones
972 2523 sgarg
        for (int i = 0; i < returnFieldList.size(); i++) {
973 3355 tao
        	String path = (String)returnFieldList.elementAt(i);
974 3769 tao
        	// Since return fileds having preicates will be handle in another path,
975
        	// we should skip it.
976
        	if (returnFieldListWithPredicates.contains(path))
977
        	{
978
        		continue;
979
        	}
980 3360 tao
        	if (path != null && path.indexOf(ATTRIBUTESYMBOL) != -1)
981 3355 tao
        	{
982
        		attributeVector.add(path);
983
        	}
984
        	else
985
        	{
986
        		elementVector.add(path);
987
        	}
988
            if(!MetaCatUtil.pathsForIndexing.contains(path)){
989
                usePathIndex = false;
990 2523 sgarg
            }
991 3355 tao
992 2523 sgarg
        }
993 3355 tao
        // check if has return field
994
        if (elementVector.size() == 0 && attributeVector.size()==0)
995
        {
996
        	return null;
997
        }
998 2073 jones
999 2523 sgarg
        if(usePathIndex){
1000 3646 leinfelder
            self.append("select docid, path, nodedata, parentnodeid, null as nodetype ");
1001 3355 tao
            self.append("from xml_path_index where path in( '");
1002 2523 sgarg
1003
            boolean firstfield = true;
1004
            //put the returnfields into the query
1005
            //the for loop allows for multiple fields
1006
            for (int i = 0; i < returnFieldList.size(); i++) {
1007
                if (firstfield) {
1008
                    firstfield = false;
1009
                    self.append( (String) returnFieldList.elementAt(i));
1010
                    self.append("' ");
1011
                }
1012
                else {
1013 3355 tao
                    self.append(", '");
1014 2523 sgarg
                    self.append( (String) returnFieldList.elementAt(i));
1015
                    self.append("' ");
1016
                }
1017
            }
1018
            self.append(") AND docid in (");
1019
            self.append(doclist);
1020
            self.append(")");
1021
1022
        } else {
1023
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1024 3634 leinfelder
            self.append("xml_nodes.parentnodeid, ");
1025
            self.append("xml_nodes.nodetype ");
1026 3355 tao
            self.append("FROM xml_index, xml_nodes WHERE (");
1027
1028 2523 sgarg
1029 3355 tao
            boolean firstElement = true;
1030
            boolean firstAttribute = true;
1031 2523 sgarg
            //put the returnfields into the query
1032
            //the for loop allows for multiple fields
1033 3355 tao
            if (elementVector.size() != 0)
1034
            {
1035
	            for (int i = 0; i < elementVector.size(); i++) {
1036
	            	String path = (String) elementVector.elementAt(i);
1037
	                if (firstElement) {
1038
	                	firstElement = false;
1039
	                	self.append(" (xml_index.nodeid=xml_nodes.parentnodeid AND xml_index.path IN ('");
1040
	                    self.append(path);
1041
	                    self.append("'");
1042
	                 }
1043
	                else
1044
	                {
1045
	                    self.append(", '");
1046
	                    self.append(path);
1047
	                    self.append("' ");
1048
	                }
1049
	            }
1050
	            self.append(") AND xml_nodes.nodetype = 'TEXT')");
1051 2523 sgarg
            }
1052 3355 tao
1053
            if (attributeVector.size() != 0)
1054
            {
1055
            	for (int j=0; j<attributeVector.size(); j++)
1056
            	{
1057
            		String path = (String) attributeVector.elementAt(j);
1058
            		if (firstAttribute)
1059
            		{
1060
            			firstAttribute = false;
1061
            			if (!firstElement)
1062
                		{
1063
                			self.append(" OR ");
1064
                		}
1065
            			self.append(" (xml_index.nodeid=xml_nodes.nodeid AND ( xml_index.path IN ( '");
1066
	                    self.append(path);
1067
	                    self.append("'");
1068
            		}
1069
            		else
1070
	                {
1071
	                    self.append(", '");
1072
	                    self.append(path);
1073
	                    self.append("' ");
1074
	                }
1075
            	}
1076
            	self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'))");
1077
            }
1078
1079
1080 2523 sgarg
            self.append(") AND xml_nodes.docid in (");
1081
            self.append(doclist);
1082 3355 tao
            self.append(")");
1083 2523 sgarg
1084
        }
1085
1086
        return self.toString();
1087 2073 jones
    }
1088
1089 2419 sgarg
1090 2073 jones
    /**
1091 2419 sgarg
     * Method to return a String generated after sorting the returnFieldList
1092
     * Vector
1093
     */
1094
    public String getSortedReturnFieldString(){
1095
        String returnFields = "";
1096
1097
        // Create a temporary vector and copy returnFieldList into it
1098
        Vector tempVector = new Vector();
1099 2464 sgarg
1100 2419 sgarg
        Iterator it = returnFieldList.iterator();
1101
        while(it.hasNext()){
1102
            tempVector.add(it.next());
1103
        }
1104
1105 3308 tao
        /*Enumeration attEnum = attributeReturnList.elements();
1106 2464 sgarg
        while(attEnum.hasMoreElements()){
1107
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1108
	    String rfield = "";
1109
            if(tempIt.hasNext()){
1110
		String element = (String)tempIt.next();
1111 2474 sgarg
		if(element != null) {
1112
		    rfield +=element;
1113 2464 sgarg
		}
1114
	    }
1115
            if(tempIt.hasNext()){
1116
		String attribute = (String)tempIt.next();
1117 2474 sgarg
		if(attribute != null) {
1118
  		    rfield = rfield + "@" + attribute;
1119 2464 sgarg
                }
1120
	    }
1121
            tempVector.add(rfield);
1122 3308 tao
        }*/
1123 2464 sgarg
1124 2419 sgarg
        // Sort the temporary vector
1125
        java.util.Collections.sort(tempVector);
1126
1127
        // Generate the string and return it
1128
        it = tempVector.iterator();
1129
        while(it.hasNext()){
1130
            returnFields = returnFields + it.next() + "|";
1131
        }
1132
        return returnFields;
1133
    }
1134
1135
1136 3355 tao
1137 2067 jones
1138 2074 jones
1139 2067 jones
    public static String printRelationSQL(String docid)
1140 1354 tao
    {
1141 2067 jones
        StringBuffer self = new StringBuffer();
1142
        self.append("select subject, relationship, object, subdoctype, ");
1143
        self.append("objdoctype from xml_relation ");
1144
        self.append("where docid like '").append(docid).append("'");
1145
        return self.toString();
1146 1354 tao
    }
1147 2066 jones
1148 2067 jones
    public static String printGetDocByDoctypeSQL(String docid)
1149
    {
1150
        StringBuffer self = new StringBuffer();
1151 465 berkley
1152 2067 jones
        self.append("SELECT docid,docname,doctype,");
1153
        self.append("date_created, date_updated ");
1154
        self.append("FROM xml_documents WHERE docid IN (");
1155
        self.append(docid).append(")");
1156
        return self.toString();
1157
    }
1158 159 jones
1159 2067 jones
    /**
1160
     * create a String description of the query that this instance represents.
1161
     * This should become a way to get the XML serialization of the query.
1162
     */
1163
    public String toString()
1164
    {
1165
        return "meta_file_id=" + meta_file_id + "\n" + query;
1166
        //DOCTITLE attr cleared from the db
1167
        //return "meta_file_id=" + meta_file_id + "\n" +
1168
        //"querytitle=" + querytitle + "\n" + query;
1169
    }
1170
1171 2073 jones
    /** A method to get rid of attribute part in path expression */
1172 2067 jones
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1173
    {
1174
        if (pathExpression == null) { return null; }
1175
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1176
        String newExpression = null;
1177 2458 cjones
        if (index != 0) {
1178 2067 jones
            newExpression = pathExpression.substring(0, index - 1);
1179
        }
1180 2663 sgarg
        logMetacat.info("The path expression without attributes: "
1181
                + newExpression);
1182 2067 jones
        return newExpression;
1183
    }
1184
1185 2073 jones
    /** A method to get attribute name from path */
1186 2067 jones
    public static String getAttributeName(String path)
1187
    {
1188
        if (path == null) { return null; }
1189
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1190
        int size = path.length();
1191
        String attributeName = null;
1192
        if (index != 1) {
1193
            attributeName = path.substring(index + 1, size);
1194
        }
1195 2663 sgarg
        logMetacat.info("The attirbute name from path: "
1196
                + attributeName);
1197 2067 jones
        return attributeName;
1198
    }
1199
1200 155 jones
}