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 349 jones
 *    Release: @release@
11 155 jones
 *
12 203 jones
 *   '$Author$'
13
 *     '$Date$'
14
 * '$Revision$'
15 669 jones
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 2 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29 155 jones
 */
30
31
package edu.ucsb.nceas.metacat;
32
33 2067 jones
import java.io.IOException;
34
import java.io.Reader;
35
import java.io.StringReader;
36
import java.util.Enumeration;
37 1354 tao
import java.util.Hashtable;
38 155 jones
import java.util.Stack;
39 158 jones
import java.util.Vector;
40 155 jones
41 2067 jones
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
42
43 2663 sgarg
import org.apache.log4j.Logger;
44 185 jones
import org.xml.sax.Attributes;
45 158 jones
import org.xml.sax.InputSource;
46
import org.xml.sax.SAXException;
47 185 jones
import org.xml.sax.XMLReader;
48 2067 jones
import org.xml.sax.helpers.DefaultHandler;
49 185 jones
import org.xml.sax.helpers.XMLReaderFactory;
50 2419 sgarg
import java.util.Iterator;
51 155 jones
52 402 berkley
/**
53 2067 jones
 * A Class that represents a structured query, and can be constructed from an
54
 * XML serialization conforming to
55 2093 tao
 *
56 2067 jones
 * @see pathquery.dtd. The printSQL() method can be used to print a SQL
57
 *      serialization of the query.
58 155 jones
 */
59 2067 jones
public class QuerySpecification extends DefaultHandler
60
{
61 1832 tao
62 2067 jones
    /** flag determining whether extended query terms are present */
63
    private boolean containsExtendedSQL = false;
64 158 jones
65 2067 jones
    /** Identifier for this query document */
66
    private String meta_file_id;
67 158 jones
68 2067 jones
    /** Title of this query */
69
    private String queryTitle;
70
71
    /** List of document types to be returned using package back tracing */
72
    private Vector returnDocList;
73
74
    /** List of document types to be searched */
75
    private Vector filterDocList;
76
77
    /** List of fields to be returned in result set */
78
    private Vector returnFieldList;
79
80
    /** List of users owning documents to be searched */
81
    private Vector ownerList;
82
83
    /** The root query group that contains the recursive query constraints */
84
    private QueryGroup query = null;
85
86
    // Query data structures used temporarily during XML parsing
87
    private Stack elementStack;
88
89
    private Stack queryStack;
90
91
    private String currentValue;
92
93
    private String currentPathexpr;
94
95
    private String parserName = null;
96
97
    private String accNumberSeparator = null;
98
99
    private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
100
101
    private boolean percentageSearch = false;
102
103
    private String userName = null;
104
105
    private static final String PUBLIC = "public";
106
107
    private String[] group = null;
108
109
    public static final String ATTRIBUTESYMBOL = "@";
110
111
    private boolean hasAttributeReturnField = false;
112
113
    private Hashtable attributeReturnList = new Hashtable();
114
115
    private int countAttributeReturnField = 0;
116
117 2093 tao
    private StringBuffer textBuffer = new StringBuffer();
118
119 2663 sgarg
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
120
121 2067 jones
    /**
122
     * construct an instance of the QuerySpecification class
123 2093 tao
     *
124 2067 jones
     * @param queryspec
125
     *            the XML representation of the query (should conform to
126
     *            pathquery.dtd) as a Reader
127
     * @param parserName
128
     *            the fully qualified name of a Java Class implementing the
129
     *            org.xml.sax.XMLReader interface
130
     */
131
    public QuerySpecification(Reader queryspec, String parserName,
132
            String accNumberSeparator) throws IOException
133
    {
134
        super();
135
136
        // Initialize the class variables
137
        returnDocList = new Vector();
138
        filterDocList = new Vector();
139
        elementStack = new Stack();
140
        queryStack = new Stack();
141
        returnFieldList = new Vector();
142
        ownerList = new Vector();
143
        this.parserName = parserName;
144
        this.accNumberSeparator = accNumberSeparator;
145
146
        // Initialize the parser and read the queryspec
147
        XMLReader parser = initializeParser();
148
        if (parser == null) {
149
            System.err.println("SAX parser not instantiated properly.");
150
        }
151
        try {
152
            parser.parse(new InputSource(queryspec));
153
        } catch (SAXException e) {
154
            System.err.println("error parsing data in "
155
                    + "QuerySpecification.QuerySpecification");
156
            System.err.println(e.getMessage());
157
        }
158 181 jones
    }
159 2067 jones
160
    /**
161
     * construct an instance of the QuerySpecification class
162 2093 tao
     *
163 2067 jones
     * @param queryspec
164
     *            the XML representation of the query (should conform to
165
     *            pathquery.dtd) as a String
166
     * @param parserName
167
     *            the fully qualified name of a Java Class implementing the
168
     *            org.xml.sax.Parser interface
169
     */
170
    public QuerySpecification(String queryspec, String parserName,
171
            String accNumberSeparator) throws IOException
172
    {
173
        this(new StringReader(queryspec), parserName, accNumberSeparator);
174 155 jones
    }
175
176 2067 jones
    /**
177
     * construct an instance of the QuerySpecification class which don't need
178
     * to parser a xml document
179 2093 tao
     *
180 2067 jones
     * @param accNumberSeparator
181
     *            the separator between doc version
182
     */
183
    public QuerySpecification(String accNumberSeparator) throws IOException
184 2045 tao
    {
185 2067 jones
        // Initialize the class variables
186
        returnDocList = new Vector();
187
        filterDocList = new Vector();
188
        elementStack = new Stack();
189
        queryStack = new Stack();
190
        returnFieldList = new Vector();
191
        ownerList = new Vector();
192
        this.accNumberSeparator = accNumberSeparator;
193 2045 tao
    }
194 2067 jones
195
    /**
196
     * Method to set user name
197 2093 tao
     *
198 2067 jones
     * @param myName
199
     *            the user name
200
     */
201
    public void setUserName(String myName)
202 2045 tao
    {
203 2067 jones
        //to lower case
204
        if (myName != null) {
205
            this.userName = myName.toLowerCase();
206
        } else {
207
            this.userName = myName;
208
        }
209 2045 tao
    }
210 2067 jones
211
    /**
212
     * Method to set user group
213 2093 tao
     *
214 2067 jones
     * @param myGroup
215
     *            the user group
216
     */
217
    public void setGroup(String[] myGroup)
218 1301 tao
    {
219 2067 jones
        this.group = myGroup;
220 1301 tao
    }
221 2067 jones
222
    /**
223
     * Method to indicate this query is a percentage search
224
     */
225
    public boolean isPercentageSearch()
226 1301 tao
    {
227 2067 jones
        return percentageSearch;
228 1301 tao
    }
229 2067 jones
230
    /*
231
     * Method to get owner query. If it is owner it has all permission
232
     */
233
    private String createOwerQuery()
234 1301 tao
    {
235 2067 jones
        String ownerQuery = null;
236
        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
237
        if (userName != null && !userName.equals("")) {
238
            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
239
        }
240 155 jones
241 2663 sgarg
        logMetacat.info("OwnerQuery: " + ownerQuery);
242 2067 jones
        return ownerQuery;
243
    }
244
245
    /*
246
     * Method to create query for xml_access, this part is to get docid list
247
     * which have a allow rule for a given user
248
     */
249
    private String createAllowRuleQuery()
250 1301 tao
    {
251 2067 jones
        String allowQuery = null;
252
        String allowString = constructAllowString();
253
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
254
        allowQuery = allowQuery + ") AND subtreeid IS NULL";
255 2663 sgarg
        logMetacat.info("allow query is: " + allowQuery);
256 2067 jones
        return allowQuery;
257
258 1301 tao
    }
259 2067 jones
260
    /* Method to construct a allow rule string */
261
    private String constructAllowString()
262 1301 tao
    {
263 2067 jones
        String allowQuery = "";
264
        // add allow rule for user name
265
        if (userName != null && !userName.equals("")) {
266
            allowQuery = allowQuery + "(lower(principal_name) = '" + userName
267
                    + "' AND perm_type = 'allow'"
268 2793 sgarg
                    + " AND (permission='4' OR permission='5' "
269
                    + "OR permission='6' OR permission='7'))";
270 2067 jones
        }
271
        // add allow rule for public
272
        allowQuery = allowQuery + "OR (lower(principal_name) = '" + PUBLIC
273
                + "' AND perm_type = 'allow'"
274 2793 sgarg
                + " AND (permission='4' OR permission='5' "
275
                + "OR permission='6' OR permission='7'))";
276 2067 jones
277
        // add allow rule for group
278
        if (group != null) {
279
            for (int i = 0; i < group.length; i++) {
280
                String groupUint = group[i];
281
                if (groupUint != null && !groupUint.equals("")) {
282
                    groupUint = groupUint.toLowerCase();
283
                    allowQuery = allowQuery + " OR (lower(principal_name) = '"
284
                            + groupUint + "' AND perm_type = 'allow'"
285 2793 sgarg
                	    + " AND (permission='4' OR permission='5' "
286
                            + "OR permission='6' OR permission='7'))";
287 2067 jones
                }//if
288
            }//for
289 1301 tao
        }//if
290 2663 sgarg
        logMetacat.info("allow string is: " + allowQuery);
291 2067 jones
        return allowQuery;
292
    }
293 155 jones
294 2067 jones
    /*
295
     * Method to create query for xml_access, this part is to get docid list
296
     * which have a deny rule and perm_order is allowFirst for a given user.
297
     * This means the user will be denied to read
298
     */
299
    private String createDenyRuleQuery()
300
    {
301
        String denyQuery = null;
302
        String denyString = constructDenyString();
303
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
304
        denyQuery = denyQuery + ") AND subtreeid IS NULL ";
305 2663 sgarg
        logMetacat.info("denyquery is: " + denyQuery);
306 2067 jones
        return denyQuery;
307 711 jones
308 2067 jones
    }
309 181 jones
310 2067 jones
    /* Construct deny string */
311
    private String constructDenyString()
312 402 berkley
    {
313 2067 jones
        String denyQuery = "";
314
        // add deny rule for user name
315
        if (userName != null && !userName.equals("")) {
316
            denyQuery = denyQuery + "(lower(principal_name) = '" + userName
317
                    + "' AND perm_type = 'deny' "
318
                    + "AND perm_order ='allowFirst'"
319 2793 sgarg
                    + " AND (permission='4' OR permission='5' "
320
                    + "OR permission='6' OR permission='7'))";
321 2067 jones
        }
322
        // add deny rule for public
323
        denyQuery = denyQuery + "OR (lower(principal_name) = '" + PUBLIC
324
                + "' AND perm_type = 'deny' " + "AND perm_order ='allowFirst'"
325 2793 sgarg
                + " AND (permission='4' OR permission='5' "
326
                + "OR permission='6' OR permission='7'))";
327 2067 jones
328
        // add allow rule for group
329
        if (group != null) {
330
            for (int i = 0; i < group.length; i++) {
331
                String groupUint = group[i];
332
                if (groupUint != null && !groupUint.equals("")) {
333
                    groupUint = groupUint.toLowerCase();
334
                    denyQuery = denyQuery + " OR (lower(principal_name) = '"
335
                            + groupUint + "' AND perm_type = 'deny' "
336
                            + "AND perm_order ='allowFirst'"
337 2793 sgarg
                	    + " AND (permission='4' OR permission='5' "
338
                            + "OR permission='6' OR permission='7'))";
339 2067 jones
                }//if
340
            }//for
341
        }//if
342
        return denyQuery;
343 402 berkley
    }
344 2067 jones
345
    /**
346
     * Method to append a access control query to SQL. So in DBQuery class, we
347
     * can get docid from both user specified query and access control query.
348
     * We don't need to checking permission after we get the doclist. It will
349
     * be good to performance
350 2093 tao
     *
351 2067 jones
     */
352
    public String getAccessQuery()
353 402 berkley
    {
354 2067 jones
        String accessQuery = null;
355
        String onwer = createOwerQuery();
356
        String allow = createAllowRuleQuery();
357
        String deny = createDenyRuleQuery();
358
        accessQuery = " AND (docid IN(" + onwer + ")";
359
        accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
360
                + " AND docid NOT IN (" + deny + ")))";
361 2663 sgarg
        logMetacat.warn("accessquery is: " + accessQuery);
362 2067 jones
        return accessQuery;
363 402 berkley
    }
364 745 jones
365 2067 jones
    /**
366
     * Returns true if the parsed query contains and extended xml query (i.e.
367
     * there is at least one &lt;returnfield&gt; in the pathquery document)
368
     */
369
    public boolean containsExtendedSQL()
370
    {
371
        if (containsExtendedSQL) {
372
            return true;
373
        } else {
374
            return false;
375
        }
376
    }
377 745 jones
378 2067 jones
    /**
379
     * A method to get if the query has an attribute return field
380
     */
381 2472 cjones
    public boolean containsAttributeReturnField()
382 2067 jones
    {
383
        return hasAttributeReturnField;
384
    }
385 745 jones
386 2067 jones
    /**
387
     * Accessor method to return the identifier of this Query
388
     */
389
    public String getIdentifier()
390
    {
391
        return meta_file_id;
392
    }
393 155 jones
394 2067 jones
    /**
395
     * method to set the identifier of this query
396
     */
397
    public void setIdentifier(String id)
398
    {
399
        this.meta_file_id = id;
400
    }
401 745 jones
402 2067 jones
    /**
403
     * Accessor method to return the title of this Query
404
     */
405
    public String getQueryTitle()
406
    {
407
        return queryTitle;
408
    }
409 745 jones
410 2067 jones
    /**
411
     * method to set the title of this query
412
     */
413
    public void setQueryTitle(String title)
414
    {
415
        this.queryTitle = title;
416
    }
417 745 jones
418 2067 jones
    /**
419
     * Accessor method to return a vector of the return document types as
420
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
421
     */
422
    public Vector getReturnDocList()
423
    {
424
        return this.returnDocList;
425
    }
426 745 jones
427 2067 jones
    /**
428
     * method to set the list of return docs of this query
429
     */
430
    public void setReturnDocList(Vector returnDocList)
431
    {
432
        this.returnDocList = returnDocList;
433
    }
434 745 jones
435 2067 jones
    /**
436
     * Accessor method to return a vector of the filter doc types as defined in
437
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
438
     */
439
    public Vector getFilterDocList()
440
    {
441
        return this.filterDocList;
442
    }
443 172 jones
444 2067 jones
    /**
445
     * method to set the list of filter docs of this query
446
     */
447
    public void setFilterDocList(Vector filterDocList)
448
    {
449
        this.filterDocList = filterDocList;
450
    }
451 155 jones
452 2067 jones
    /**
453
     * Accessor method to return a vector of the extended return fields as
454
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
455
     */
456
    public Vector getReturnFieldList()
457
    {
458
        return this.returnFieldList;
459
    }
460 155 jones
461 2067 jones
    /**
462
     * method to set the list of fields to be returned by this query
463
     */
464
    public void setReturnFieldList(Vector returnFieldList)
465
    {
466
        this.returnFieldList = returnFieldList;
467
    }
468 155 jones
469 2067 jones
    /**
470
     * Accessor method to return a vector of the owner fields as defined in the
471
     * &lt;owner&gt; tag in the pathquery dtd.
472
     */
473
    public Vector getOwnerList()
474
    {
475
        return this.ownerList;
476
    }
477 155 jones
478 2067 jones
    /**
479
     * method to set the list of owners used to constrain this query
480
     */
481
    public void setOwnerList(Vector ownerList)
482
    {
483
        this.ownerList = ownerList;
484 155 jones
    }
485
486 2067 jones
    /**
487
     * get the QueryGroup used to express query constraints
488
     */
489
    public QueryGroup getQueryGroup()
490
    {
491
        return query;
492 158 jones
    }
493 155 jones
494 2067 jones
    /**
495
     * set the querygroup
496
     */
497
    public void setQueryGroup(QueryGroup group)
498
    {
499
        query = group;
500 158 jones
    }
501
502 2067 jones
    /**
503
     * set if this query sepcification has extendQuery(has return doc type or
504
     * not)
505
     */
506
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
507
    {
508
        containsExtendedSQL = hasExtenedQuery;
509
    }
510 158 jones
511 2067 jones
    /**
512
     * Set up the SAX parser for reading the XML serialized query
513
     */
514
    private XMLReader initializeParser()
515
    {
516
        XMLReader parser = null;
517
518
        // Set up the SAX document handlers for parsing
519
        try {
520
521
            // Get an instance of the parser
522
            parser = XMLReaderFactory.createXMLReader(parserName);
523
524
            // Set the ContentHandler to this instance
525
            parser.setContentHandler(this);
526
527
            // Set the error Handler to this instance
528
            parser.setErrorHandler(this);
529
530
        } catch (Exception e) {
531
            System.err.println("Error in QuerySpcecification.initializeParser "
532
                    + e.toString());
533
        }
534
535
        return parser;
536 1833 tao
    }
537 170 jones
538 2067 jones
    /**
539
     * callback method used by the SAX Parser when the start tag of an element
540
     * is detected. Used in this context to parse and store the query
541
     * information in class variables.
542
     */
543
    public void startElement(String uri, String localName, String qName,
544
            Attributes atts) throws SAXException
545
    {
546
        BasicNode currentNode = new BasicNode(localName);
547
        // add attributes to BasicNode here
548
        if (atts != null) {
549
            int len = atts.getLength();
550
            for (int i = 0; i < len; i++) {
551
                currentNode
552
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
553
            }
554
        }
555 170 jones
556 2067 jones
        elementStack.push(currentNode);
557
        if (currentNode.getTagName().equals("querygroup")) {
558
            QueryGroup currentGroup = new QueryGroup(currentNode
559
                    .getAttribute("operator"));
560
            if (query == null) {
561
                query = currentGroup;
562
            } else {
563
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
564
                parentGroup.addChild(currentGroup);
565
            }
566
            queryStack.push(currentGroup);
567
        }
568
    }
569 172 jones
570 2067 jones
    /**
571
     * callback method used by the SAX Parser when the end tag of an element is
572
     * detected. Used in this context to parse and store the query information
573
     * in class variables.
574
     */
575
    public void endElement(String uri, String localName, String qName)
576
            throws SAXException
577
    {
578
        BasicNode leaving = (BasicNode) elementStack.pop();
579
        if (leaving.getTagName().equals("queryterm")) {
580
            boolean isCaseSensitive = (new Boolean(leaving
581
                    .getAttribute("casesensitive"))).booleanValue();
582
            QueryTerm currentTerm = null;
583
            if (currentPathexpr == null) {
584
                currentTerm = new QueryTerm(isCaseSensitive, leaving
585
                        .getAttribute("searchmode"), currentValue);
586
            } else {
587
                currentTerm = new QueryTerm(isCaseSensitive, leaving
588
                        .getAttribute("searchmode"), currentValue,
589
                        currentPathexpr);
590
            }
591
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
592
            currentGroup.addChild(currentTerm);
593
            currentValue = null;
594
            currentPathexpr = null;
595
        } else if (leaving.getTagName().equals("querygroup")) {
596
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
597 2093 tao
        } else if (leaving.getTagName().equals("meta_file_id")) {
598
              meta_file_id = textBuffer.toString().trim();
599
        } else if (leaving.getTagName().equals("querytitle")) {
600
              queryTitle = textBuffer.toString().trim();
601
        } else if (leaving.getTagName().equals("value")) {
602
              currentValue = textBuffer.toString().trim();
603
        } else if (leaving.getTagName().equals("pathexpr")) {
604
              currentPathexpr = textBuffer.toString().trim();
605
        } else if (leaving.getTagName().equals("returndoctype")) {
606
              returnDocList.add(textBuffer.toString().trim());
607
        } else if (leaving.getTagName().equals("filterdoctype")) {
608
              filterDocList.add(textBuffer.toString().trim());
609
        } else if (leaving.getTagName().equals("returnfield")) {
610
              handleReturnField(textBuffer.toString().trim());
611
        } else if (leaving.getTagName().equals("filterdoctype")) {
612
              filterDocList.add(textBuffer.toString().trim());
613
        } else if (leaving.getTagName().equals("owner")) {
614
              ownerList.add(textBuffer.toString().trim());
615 172 jones
        }
616 2093 tao
617
        //rest textBuffer
618
        textBuffer = null;
619
        textBuffer = new StringBuffer();
620
621 172 jones
    }
622 743 jones
623 2067 jones
    /**
624
     * callback method used by the SAX Parser when the text sequences of an xml
625
     * stream are detected. Used in this context to parse and store the query
626
     * information in class variables.
627
     */
628
    public void characters(char ch[], int start, int length)
629
    {
630 2093 tao
      // buffer all text nodes for same element. This is for text was splited
631
      // into different nodes
632
      textBuffer.append(new String(ch, start, length));
633 2067 jones
634
    }
635
636
    /**
637
     * Method to transfer string to return field
638
     */
639
    public void handleReturnField(String inputString)
640
    {
641
        // make sure if return fields has an attribute or not
642
        if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
643
            // no attribute value will be returned
644 2663 sgarg
            logMetacat.info("QuerySpecification.handleReturnField(): " );
645
            logMetacat.info("  there are no attributes in the XPATH statement" );
646 2067 jones
            returnFieldList.add(inputString);
647
            containsExtendedSQL = true;
648 535 jones
        } else {
649 2474 sgarg
650 2467 cjones
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
651 2474 sgarg
652 2467 cjones
            // case where the return field is solely an attribute
653 2663 sgarg
            logMetacat.info("QuerySpecification.handleReturnField(): " );
654
            logMetacat.info("  there are *only* attributes in the XPATH statement" );
655 2472 cjones
            String returnPath = newPathExpressionWithOutAttribute(inputString);
656
            String attributeName = getAttributeName(inputString);
657
            Vector pathInfo = new Vector();
658
            // the vector has the information about return path and
659
            // attributename
660
            pathInfo.addElement(returnPath);
661
            pathInfo.addElement(attributeName);
662
            // put the vector into a hash table. The reseaon why don't put
663
            // return path or attributename as a key is because they are not
664
            // unique
665
            attributeReturnList.put(new Integer(countAttributeReturnField),
666
                    pathInfo);
667
            countAttributeReturnField++;
668
            hasAttributeReturnField = true;
669 2467 cjones
            containsExtendedSQL = true;
670
          } else {
671 2067 jones
            // has a attribute return field
672
            // divied the return filed into two parts, one is path and the
673
            // other is attribue name
674 2663 sgarg
            logMetacat.info("QuerySpecification.handleReturnField: " );
675
            logMetacat.info("  there are both attributes and elements" );
676
            logMetacat.info("  in the XPATH statement" );
677 2067 jones
            String returnPath = newPathExpressionWithOutAttribute(inputString);
678
            String attributeName = getAttributeName(inputString);
679
            Vector pathInfo = new Vector();
680
            // the vector has the information about return path and
681
            // attributename
682
            pathInfo.addElement(returnPath);
683
            pathInfo.addElement(attributeName);
684
            // put the vector into a hash table. The reseaon why don't put
685
            // return path or attributename as a key is because they are not
686
            // unique
687
            attributeReturnList.put(new Integer(countAttributeReturnField),
688
                    pathInfo);
689
            countAttributeReturnField++;
690
            hasAttributeReturnField = true;
691
            containsExtendedSQL = true;
692 2467 cjones
          }
693 535 jones
        }
694
    }
695
696 2067 jones
    /**
697
     * create a SQL serialization of the query that this instance represents
698
     */
699
    public String printSQL(boolean useXMLIndex)
700
    {
701
702
        StringBuffer self = new StringBuffer();
703 2366 sgarg
        StringBuffer queryString = new StringBuffer();
704 2067 jones
705 2366 sgarg
        queryString.append("SELECT docid,docname,doctype,");
706
        queryString.append("date_created, date_updated, rev ");
707
        queryString.append("FROM xml_documents WHERE");
708 2067 jones
709 2366 sgarg
        // Get the query from the QueryGroup and check
710
        // if no query has been returned
711
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
712 2677 sgarg
        logMetacat.info("Query from query in QuerySpec.printSQL: "
713
        		+ queryFromQueryGroup);
714
715 2373 sgarg
        if(!queryFromQueryGroup.trim().equals("")){
716 2366 sgarg
            self.append(" docid IN (");
717 2373 sgarg
            self.append(queryFromQueryGroup);
718 2366 sgarg
            self.append(") ");
719
        }
720 2067 jones
721
        // Add SQL to filter for doctypes requested in the query
722
        // This is an implicit OR for the list of doctypes. Only doctypes in
723
        // this
724
        // list will be searched if the tag is present
725
        if (!filterDocList.isEmpty()) {
726
            boolean firstdoctype = true;
727 2366 sgarg
            boolean emptyString = true;
728
729
            if(!self.toString().equals("")){
730
                self.append(" AND (");
731
                emptyString = false;
732
            }
733
734 2067 jones
            Enumeration en = filterDocList.elements();
735
            while (en.hasMoreElements()) {
736
                String currentDoctype = (String) en.nextElement();
737
                if (firstdoctype) {
738
                    firstdoctype = false;
739
                    self.append(" doctype = '" + currentDoctype + "'");
740
                } else {
741
                    self.append(" OR doctype = '" + currentDoctype + "'");
742
                }
743
            }
744 2366 sgarg
745
            if(!emptyString){
746
                self.append(") ");
747
            }
748 535 jones
        }
749 2067 jones
750
        // Add SQL to filter for owners requested in the query
751
        // This is an implicit OR for the list of owners
752
        if (!ownerList.isEmpty()) {
753
            boolean first = true;
754 2366 sgarg
            boolean emptyString = true;
755
756
            if(!self.toString().equals("")){
757
                self.append(" AND (");
758
                emptyString = false;
759
            }
760
761 2067 jones
            Enumeration en = ownerList.elements();
762
            while (en.hasMoreElements()) {
763
                String current = (String) en.nextElement();
764
                if (current != null) {
765
                    current = current.toLowerCase();
766
                }
767
                if (first) {
768
                    first = false;
769
                    self.append(" lower(user_owner) = '" + current + "'");
770
                } else {
771
                    self.append(" OR lower(user_owner) = '" + current + "'");
772
                }
773
            }
774 2366 sgarg
775
            if(!emptyString){
776
                self.append(") ");
777
            }
778 2067 jones
        }
779
780
        // if there is only one percentage search item, this query is a
781
        // percentage
782
        // search query
783 2663 sgarg
        logMetacat.info("percentage number: "
784
                + query.getPercentageSymbolCount());
785 2067 jones
        if (query.getPercentageSymbolCount() == 1) {
786 2663 sgarg
            logMetacat.info("It is a percentage search");
787 2067 jones
            percentageSearch = true;
788
        }
789
790 2366 sgarg
        queryString.append(self.toString());
791
        return queryString.toString();
792 535 jones
    }
793 2067 jones
794
    /**
795
     * This sql command will selecet startnodeid and endnodeid that user can
796
     * NOT access
797
     */
798
    public String printAccessControlSQLForReturnField(String doclist)
799 1350 tao
    {
800 2067 jones
        StringBuffer sql = new StringBuffer();
801
        String allowString = constructAllowString();
802
        String denyString = constructDenyString();
803
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
804
        sql.append("WHERE docid in (");
805
        sql.append(doclist);
806
        sql.append(") AND startnodeid IS NOT NULL AND ");
807
        sql.append("(");
808
        sql.append("(");
809 2068 jones
        sql
810
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
811 2067 jones
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
812
        sql.append(" AND lower(xml_documents.user_owner) ='");
813
        sql.append(userName);
814
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
815
        sql.append(")");
816
        sql.append(" AND ");
817
        sql.append("(");
818 2068 jones
        sql
819
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
820 2067 jones
        sql.append(allowString);
821
        sql.append(") AND (startnodeid IS NOT NULL))");
822
        sql.append(")");
823 2068 jones
        sql
824
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
825 2067 jones
        sql.append(denyString);
826
        sql.append(") AND (startnodeid IS NOT NULL))");
827
        sql.append(")");
828
        sql.append(")");
829
        sql.append(")");
830 2663 sgarg
        logMetacat.info("accessControlSQLForReturnField: "
831
                + sql.toString());
832 2067 jones
        return sql.toString();
833 1350 tao
    }
834 2067 jones
835
    /**
836
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
837 2069 jones
     * pathquery document. This allows for customization of the returned fields.
838 2093 tao
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
839
     * xml_nodes to find the fields to be included by their path expression, and
840 2069 jones
     * avoids the use of the xml_index table.
841 2093 tao
     *
842 2073 jones
     * @param doclist the list of document ids to search
843 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id) which
844 2073 jones
     *            this user should not access
845 2093 tao
     * @param useXMLIndex a boolean flag indicating whether to search using
846 2073 jones
     *            xml_index
847 2069 jones
     */
848
    public String printExtendedSQL(String doclist,
849
            Hashtable unaccessableNodePair, boolean useXMLIndex)
850
    {
851
        if (useXMLIndex) {
852
            return printExtendedSQL(doclist, unaccessableNodePair);
853
        } else {
854
            StringBuffer self = new StringBuffer();
855
856
            boolean firstfield = true;
857
            //put the returnfields into the query
858
            //the for loop allows for multiple fields
859
            for (int i = 0; i < returnFieldList.size(); i++) {
860
                if (firstfield) {
861
                    firstfield = false;
862
                } else {
863 2093 tao
                    self.append(" UNION ");
864 2069 jones
                }
865
                String path  = (String) returnFieldList.elementAt(i);
866
                self.append("select xml_nodes.docid, ");
867 2073 jones
                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
868 2069 jones
                self.append("xml_nodes.parentnodeid ");
869
                self.append("from xml_nodes, xml_documents ");
870
                self.append("where parentnodeid IN ");
871
                self.append(QueryTerm.useNestedStatements(path));
872 2093 tao
873 2069 jones
                self.append(" AND xml_nodes.docid in (");
874
                self.append(doclist);
875 2073 jones
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
876 2069 jones
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
877 2093 tao
878 2073 jones
                addAccessRestrictionSQL(unaccessableNodePair, self);
879 2069 jones
            }
880
881
            return self.toString();
882
        }
883
    }
884 2093 tao
885 2069 jones
    /**
886
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
887
     * pathquery document. This allows for customization of the returned fields.
888
     * It uses the xml_index table and so assumes that this table has been
889
     * built.
890 2093 tao
     *
891 2073 jones
     * @param doclist the list of document ids to search
892 2093 tao
     * @param unaccessableNodePair the node pairs (start id and end id)
893 2073 jones
     *            which this user should not access
894 2067 jones
     */
895 2093 tao
    public String printExtendedSQL(String doclist,
896 2067 jones
            Hashtable unaccessableNodePair)
897 402 berkley
    {
898 2663 sgarg
        logMetacat.info("querySpecification.printExtendedSQL called\n");
899 2067 jones
        StringBuffer self = new StringBuffer();
900 2472 cjones
901 2523 sgarg
        boolean usePathIndex = true;
902 2434 sgarg
903 2523 sgarg
        // test if the are elements in the return fields
904
        if ( returnFieldList.size() == 0 ) {
905
            return null;
906
        }
907 2067 jones
908 2523 sgarg
        for (int i = 0; i < returnFieldList.size(); i++) {
909
            if(!MetaCatUtil.pathsForIndexing.contains
910
               (returnFieldList.elementAt(i))){
911
                usePathIndex = false;
912
                break;
913
            }
914
        }
915 2073 jones
916 2523 sgarg
        if(usePathIndex){
917
            self.append("select docid, path, nodedata, parentnodeid ");
918
            self.append("from xml_path_index where (path like '");
919
920
            boolean firstfield = true;
921
            //put the returnfields into the query
922
            //the for loop allows for multiple fields
923
            for (int i = 0; i < returnFieldList.size(); i++) {
924
                if (firstfield) {
925
                    firstfield = false;
926
                    self.append( (String) returnFieldList.elementAt(i));
927
                    self.append("' ");
928
                }
929
                else {
930
                    self.append("or path like '");
931
                    self.append( (String) returnFieldList.elementAt(i));
932
                    self.append("' ");
933
                }
934
            }
935
            self.append(") AND docid in (");
936
            self.append(doclist);
937
            self.append(")");
938
939
        } else {
940
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
941
            self.append("xml_nodes.parentnodeid ");
942
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
943
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
944
945
            boolean firstfield = true;
946
            //put the returnfields into the query
947
            //the for loop allows for multiple fields
948
            for (int i = 0; i < returnFieldList.size(); i++) {
949
                if (firstfield) {
950
                    firstfield = false;
951
                    self.append((String) returnFieldList.elementAt(i));
952
                    self.append("' ");
953
                } else {
954
                    self.append("or xml_index.path like '");
955
                    self.append((String) returnFieldList.elementAt(i));
956
                    self.append("' ");
957
                }
958
            }
959
            self.append(") AND xml_nodes.docid in (");
960
            self.append(doclist);
961
            self.append(") AND xml_nodes.nodetype = 'TEXT'");
962
963
        }
964
965
        addAccessRestrictionSQL(unaccessableNodePair, self);
966
967
        return self.toString();
968 2073 jones
    }
969
970 2419 sgarg
971 2073 jones
    /**
972 2419 sgarg
     * Method to return a String generated after sorting the returnFieldList
973
     * Vector
974
     */
975
    public String getSortedReturnFieldString(){
976
        String returnFields = "";
977
978
        // Create a temporary vector and copy returnFieldList into it
979
        Vector tempVector = new Vector();
980 2464 sgarg
981 2419 sgarg
        Iterator it = returnFieldList.iterator();
982
        while(it.hasNext()){
983
            tempVector.add(it.next());
984
        }
985
986 2464 sgarg
        Enumeration attEnum = attributeReturnList.elements();
987
        while(attEnum.hasMoreElements()){
988
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
989
	    String rfield = "";
990
            if(tempIt.hasNext()){
991
		String element = (String)tempIt.next();
992 2474 sgarg
		if(element != null) {
993
		    rfield +=element;
994 2464 sgarg
		}
995
	    }
996
            if(tempIt.hasNext()){
997
		String attribute = (String)tempIt.next();
998 2474 sgarg
		if(attribute != null) {
999
  		    rfield = rfield + "@" + attribute;
1000 2464 sgarg
                }
1001
	    }
1002
            tempVector.add(rfield);
1003
        }
1004
1005 2419 sgarg
        // Sort the temporary vector
1006
        java.util.Collections.sort(tempVector);
1007
1008
        // Generate the string and return it
1009
        it = tempVector.iterator();
1010
        while(it.hasNext()){
1011
            returnFields = returnFields + it.next() + "|";
1012
        }
1013
        return returnFields;
1014
    }
1015
1016
1017
    /**
1018 2073 jones
     * Create the SQl necessary to restrict access to allowed nodes.  This is
1019
     * accomplished by restricting the nodes that are returned to include
1020
     * only those whose IDs fall outside of a set of start/stop pairs of
1021
     * nodeid values.  These pairs are passed in as a hash, with the key
1022
     * containing the start nodeid and the value containing the end nodeid.
1023
     * Any nodes between these start and end nodeid values will be excluded
1024
     * from the results.
1025 2093 tao
     *
1026 2073 jones
     * @param unaccessableNodePair hash of start/end nodeid pairs to restrict
1027 2093 tao
     * @param self a stringbuffer to which the genrated SQL is appended
1028 2073 jones
     */
1029 2093 tao
    private void addAccessRestrictionSQL(Hashtable unaccessableNodePair,
1030 2073 jones
            StringBuffer self)
1031
    {
1032 2067 jones
        // add control part for extended query
1033
        Enumeration en = unaccessableNodePair.keys();
1034
1035
        while (en.hasMoreElements()) {
1036
            // Get control pairs in object
1037
            Long startNodeIdObject = (Long) en.nextElement();
1038
            Long endNodeIdObject = (Long) unaccessableNodePair
1039
                    .get(startNodeIdObject);
1040
            // change it to long
1041
            long startNodeId = startNodeIdObject.longValue();
1042
            long endNodeId = endNodeIdObject.longValue();
1043
            // add into query
1044 2073 jones
            self.append(" AND ( xml_nodes.nodeid < ");
1045 2067 jones
            self.append(startNodeId);
1046
            self.append(" OR xml_nodes.nodeid > ");
1047
            self.append(endNodeId);
1048
            self.append(")");
1049
        }
1050 402 berkley
    }
1051 2074 jones
1052 2067 jones
    /**
1053 2074 jones
     * This method prints sql that finds the values of attributes in the xml
1054
     * documents based upon the whether the returnfield tag in the pathquery
1055 2093 tao
     * document has an attribute symbol (@). This allows for customization of
1056 2074 jones
     * the returned fields.
1057 2093 tao
     *
1058 2074 jones
     * @param doclist the list of document ids to search
1059 2093 tao
     * @param useXMLIndex a boolean flag indicating whether to search using
1060 2074 jones
     *            xml_index
1061 2067 jones
     */
1062 2074 jones
    public String printAttributeQuery(String doclist, boolean useXMLIndex)
1063
    {
1064
        if (useXMLIndex) {
1065
            return printAttributeQuery(doclist);
1066
        } else {
1067
            StringBuffer self = new StringBuffer();
1068
            boolean firstfield = true;
1069
            //put the returnfields attributes into the query
1070
            //the for loop allows for multiple fields and attributes
1071
            Enumeration returnAttributes = attributeReturnList.elements();
1072
            while (returnAttributes.hasMoreElements()) {
1073
                Vector currentVector = (Vector) returnAttributes.nextElement();
1074
                String returnPath = (String) currentVector.elementAt(0);
1075
                String attributeName = (String) currentVector.elementAt(1);
1076
                if (firstfield) {
1077
                    firstfield = false;
1078
                } else {
1079 2093 tao
                    self.append(" UNION ");
1080 2074 jones
                }
1081
                self.append("select xml_nodes.docid, '");
1082
                self.append(returnPath);
1083
                self.append("' as path, xml_nodes.nodedata, xml_nodes.nodename ");
1084
                self.append("from xml_nodes, xml_documents ");
1085
                self.append("where parentnodeid IN ");
1086
                self.append(QueryTerm.useNestedStatements(returnPath));
1087
                self.append(" AND xml_nodes.nodename like '");
1088
                self.append(attributeName);
1089
                self.append("' AND xml_nodes.docid in (");
1090
                self.append(doclist);
1091
                self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1092 2078 jones
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
1093 2074 jones
            }
1094 2093 tao
1095 2663 sgarg
            logMetacat.warn("Attribute query: " + self.toString());
1096 2074 jones
1097 2093 tao
            return self.toString();
1098 2074 jones
        }
1099
    }
1100
1101
    /**
1102
     * This method prints sql that finds the values of attributes in the xml
1103
     * documents based upon the whether the returnfield tag in the pathquery
1104 2093 tao
     * document has an attribute symbol (@). This allows for customization of
1105 2074 jones
     * the returned fields.
1106 2093 tao
     *
1107 2074 jones
     * @param doclist the list of document ids to search
1108
     */
1109 2067 jones
    public String printAttributeQuery(String doclist)
1110 1449 tao
    {
1111 2067 jones
        StringBuffer self = new StringBuffer();
1112
        self.append("select xml_nodes.docid, xml_index.path, ");
1113
        self.append("xml_nodes.nodedata, xml_nodes.nodename ");
1114
        self.append("from xml_index, xml_nodes where xml_index.nodeid=");
1115
        self.append("xml_nodes.parentnodeid and (");
1116
        boolean firstfield = true;
1117
        //put the returnfields attributes into the query
1118
        //the for loop allows for multiple fields and attributes
1119
        Enumeration returnAttributes = attributeReturnList.elements();
1120
        while (returnAttributes.hasMoreElements()) {
1121
            Vector currentVector = (Vector) returnAttributes.nextElement();
1122
            String returnPath = (String) currentVector.elementAt(0);
1123
            String attributeName = (String) currentVector.elementAt(1);
1124
            if (firstfield) {
1125
                firstfield = false;
1126 2466 sgarg
                self.append("( ");
1127
                if(returnPath != null){
1128
                    self.append("xml_index.path like '");
1129
                    self.append(returnPath);
1130
                    self.append("' AND ");
1131 2472 cjones
                }else {
1132 2663 sgarg
                  logMetacat.info("QuerySpecification.printAttributeQuery: "
1133
                   + "returnPath is: " + returnPath);
1134 2466 sgarg
                }
1135
                self.append("xml_nodes.nodename like '");
1136 2067 jones
                self.append(attributeName);
1137
                self.append("') ");
1138
            } else {
1139 2466 sgarg
                self.append(" or (");
1140
                if(returnPath != null){
1141
                    self.append("xml_index.path like '");
1142
                    self.append(returnPath);
1143
                    self.append("' AND ");
1144 2472 cjones
                }else {
1145 2663 sgarg
                  logMetacat.info("QuerySpecification.printAttributeQuery: "
1146
                   + "returnPath is null: " + returnPath);
1147 2466 sgarg
                }
1148
                self.append("xml_nodes.nodename like '");
1149 2067 jones
                self.append(attributeName);
1150
                self.append("') ");
1151
            }
1152
        }
1153
        self.append(") AND xml_nodes.docid in (");
1154
        self.append(doclist);
1155 2074 jones
        self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1156 2663 sgarg
        logMetacat.warn("Attribute query: " + self.toString());
1157 2067 jones
1158
        return self.toString();
1159 1449 tao
    }
1160 405 berkley
1161 2067 jones
    public static String printRelationSQL(String docid)
1162 1354 tao
    {
1163 2067 jones
        StringBuffer self = new StringBuffer();
1164
        self.append("select subject, relationship, object, subdoctype, ");
1165
        self.append("objdoctype from xml_relation ");
1166
        self.append("where docid like '").append(docid).append("'");
1167
        return self.toString();
1168 1354 tao
    }
1169 2066 jones
1170 2067 jones
    public static String printGetDocByDoctypeSQL(String docid)
1171
    {
1172
        StringBuffer self = new StringBuffer();
1173 465 berkley
1174 2067 jones
        self.append("SELECT docid,docname,doctype,");
1175
        self.append("date_created, date_updated ");
1176
        self.append("FROM xml_documents WHERE docid IN (");
1177
        self.append(docid).append(")");
1178
        return self.toString();
1179
    }
1180 159 jones
1181 2067 jones
    /**
1182
     * create a String description of the query that this instance represents.
1183
     * This should become a way to get the XML serialization of the query.
1184
     */
1185
    public String toString()
1186
    {
1187
        return "meta_file_id=" + meta_file_id + "\n" + query;
1188
        //DOCTITLE attr cleared from the db
1189
        //return "meta_file_id=" + meta_file_id + "\n" +
1190
        //"querytitle=" + querytitle + "\n" + query;
1191
    }
1192
1193 2073 jones
    /** A method to get rid of attribute part in path expression */
1194 2067 jones
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1195
    {
1196
        if (pathExpression == null) { return null; }
1197
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1198
        String newExpression = null;
1199 2458 cjones
        if (index != 0) {
1200 2067 jones
            newExpression = pathExpression.substring(0, index - 1);
1201
        }
1202 2663 sgarg
        logMetacat.info("The path expression without attributes: "
1203
                + newExpression);
1204 2067 jones
        return newExpression;
1205
    }
1206
1207 2073 jones
    /** A method to get attribute name from path */
1208 2067 jones
    public static String getAttributeName(String path)
1209
    {
1210
        if (path == null) { return null; }
1211
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1212
        int size = path.length();
1213
        String attributeName = null;
1214
        if (index != 1) {
1215
            attributeName = path.substring(index + 1, size);
1216
        }
1217 2663 sgarg
        logMetacat.info("The attirbute name from path: "
1218
                + attributeName);
1219 2067 jones
        return attributeName;
1220
    }
1221
1222 155 jones
}