Index: src/edu/ucsb/nceas/metacat/QuerySpecification.java
===================================================================
--- src/edu/ucsb/nceas/metacat/QuerySpecification.java	(revision 3234)
+++ src/edu/ucsb/nceas/metacat/QuerySpecification.java	(revision 3235)
@@ -61,6 +61,9 @@
     /** flag determining whether extended query terms are present */
     private boolean containsExtendedSQL = false;
 
+    /** flag determining whether predicates are present */
+    private boolean containsPredicates = false;
+
     /** Identifier for this query document */
     private String meta_file_id;
 
@@ -107,6 +110,10 @@
 
     public static final String ATTRIBUTESYMBOL = "@";
 
+    public static final char PREDICATE_START = '[';
+
+    public static final char PREDICATE_END = ']';
+
     private boolean hasAttributeReturnField = false;
 
     private Hashtable attributeReturnList = new Hashtable();
@@ -626,7 +633,6 @@
         }
 
         //rest textBuffer
-        textBuffer = null;
         textBuffer = new StringBuffer();
 
     }
@@ -647,7 +653,7 @@
     /**
      * Method to transfer string to return field
      */
-    public void handleReturnField(String inputString)
+    /* public void handleReturnField(String inputString)
     {
         // make sure if return fields has an attribute or not
         if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
@@ -703,7 +709,82 @@
           }
         }
     }
+     */
+    public void handleReturnField(String inputString)
+    {
+        int attributePos = inputString.indexOf(ATTRIBUTESYMBOL);
+        int predicateStart = -1;
+        int predicateEnd;
+        boolean hasPredicate = false;
 
+        while (true)
+        {
+            predicateStart = inputString.indexOf(PREDICATE_START, predicateStart + 1);
+
+            if (attributePos == -1)
+                break;
+
+            if (predicateStart == -1)
+                break;
+
+            hasPredicate = true;
+
+            if (attributePos < predicateStart)
+                break;
+
+            predicateEnd = inputString.indexOf(PREDICATE_END, predicateStart);
+
+            if (predicateEnd == -1)
+            {
+                logMetacat.warn("handleReturnField(): ");
+                logMetacat.warn("    Invalid path: " + inputString);
+                return;
+            }
+
+            while (attributePos < predicateEnd)
+            {
+                attributePos = inputString.indexOf(ATTRIBUTESYMBOL, attributePos + 1);
+
+                if (attributePos == -1)
+                    break;
+            }
+        }
+
+        if (hasPredicate)
+            containsPredicates = true;
+
+        containsExtendedSQL = true;
+
+        // make sure if return fields has an attribute or not
+        if (attributePos == -1)
+        {
+            // no attribute value will be returned
+            logMetacat.info("QuerySpecification.handleReturnField(): " );
+            logMetacat.info("  there are no attributes in the XPATH statement" );
+            returnFieldList.add(inputString);
+            return;
+        }
+
+        // case where the return field is solely an attribute
+        logMetacat.info("QuerySpecification.handleReturnField(): " );
+        logMetacat.info("  Found an attribute in the XPATH statement." );
+        String returnPath = inputString.substring(0,
+                attributePos == 0 ? attributePos : attributePos - 1);
+        String attributeName = inputString.substring(attributePos + 1).trim();
+        Vector pathInfo = new Vector();
+        // the vector has the information about return path and
+        // attributename
+        pathInfo.addElement(returnPath);
+        pathInfo.addElement(attributeName);
+        // put the vector into a hash table. The reseaon why don't put
+        // return path or attributename as a key is because they are not
+        // unique
+        attributeReturnList.put(new Integer(countAttributeReturnField),
+                pathInfo);
+        countAttributeReturnField++;
+        hasAttributeReturnField = true;
+    }
+
     /**
      * create a SQL serialization of the query that this instance represents
      */
@@ -859,23 +940,30 @@
     public String printExtendedSQL(String doclist,
             Hashtable unaccessableNodePair, boolean useXMLIndex)
     {
-        if (useXMLIndex) {
+        if (useXMLIndex && !containsPredicates)
+        {
             return printExtendedSQL(doclist, unaccessableNodePair);
-        } else {
+        }
+        else
+        {
             StringBuffer self = new StringBuffer();
 
             boolean firstfield = true;
             //put the returnfields into the query
             //the for loop allows for multiple fields
-            for (int i = 0; i < returnFieldList.size(); i++) {
-                if (firstfield) {
+            for (int i = 0; i < returnFieldList.size(); i++)
+            {
+                if (firstfield)
+                {
                     firstfield = false;
-                } else {
+                }
+                else
+                {
                     self.append(" UNION ");
                 }
                 String path  = (String) returnFieldList.elementAt(i);
                 self.append("select xml_nodes.docid, ");
-                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
+                self.append("'"+ path.replaceAll("'", "''") + "' as path, xml_nodes.nodedata, ");
                 self.append("xml_nodes.parentnodeid ");
                 self.append("from xml_nodes, xml_documents ");
                 self.append("where parentnodeid IN ");
@@ -1072,7 +1160,7 @@
      */
     public String printAttributeQuery(String doclist, boolean useXMLIndex)
     {
-        if (useXMLIndex) {
+        if (useXMLIndex && !containsPredicates) {
             return printAttributeQuery(doclist);
         } else {
             StringBuffer self = new StringBuffer();
@@ -1090,7 +1178,7 @@
                     self.append(" UNION ");
                 }
                 self.append("select xml_nodes.docid, '");
-                self.append(returnPath);
+                self.append(returnPath.replaceAll("'", "''"));
                 self.append("' as path, xml_nodes.nodedata, xml_nodes.nodename ");
                 self.append("from xml_nodes, xml_documents ");
                 self.append("where parentnodeid IN ");
Index: src/edu/ucsb/nceas/metacat/QueryTerm.java
===================================================================
--- src/edu/ucsb/nceas/metacat/QueryTerm.java	(revision 3234)
+++ src/edu/ucsb/nceas/metacat/QueryTerm.java	(revision 3235)
@@ -30,10 +30,12 @@
 package edu.ucsb.nceas.metacat;
 
 import java.util.Vector;
+import org.apache.log4j.Logger;
 
 /** a utility class that represents a single term in a query */
 public class QueryTerm
 {
+    private static Logger log = Logger.getLogger(QueryTerm.class);
 
     private boolean casesensitive = false;
 
@@ -286,10 +288,9 @@
         }
     }
 
-    /*
+    /**
      * Constraint the query with @pathexp without using the XML Index, but
      * nested SQL statements instead. The query migth be slower.
-     */
     public static String useNestedStatements(String pathexpr)
     {
       System.out.println("pathexpr: " + pathexpr);
@@ -310,7 +311,7 @@
         for (i = 0; i < nodes.size() - 1; i++) {
             nestedStmts.append("(SELECT nodeid FROM xml_nodes"
                     + " WHERE nodename LIKE '" + (String) nodes.elementAt(i)
-                    + "'" + " AND parentnodeid IN ");
+                    + "' AND parentnodeid IN ");
         }
         // for the last statement: it is without " AND parentnodeid IN "
         nestedStmts.append("(SELECT nodeid FROM xml_nodes"
@@ -322,8 +323,228 @@
 
         return nestedStmts.toString();
     }
+     */
+    public static String useNestedStatements(String pathexpr)
+    {
+        log.info("useNestedStatements()");
+        log.info("pathexpr: " + pathexpr);
+        StringBuffer nestedStmts = new StringBuffer();
+        String path = pathexpr.trim();
 
+        if (path.indexOf('/') == 0)
+        {
+            nestedStmts.append("AND parentnodeid = rootnodeid ");
+            path = path.substring(1).trim();
+        }
+
+        do
+        {
+            int inx = path.indexOf('/');
+            int predicateStart = -1;
+            int predicateEnd;
+            String node;
+            Vector predicates = new Vector();
+
+            // extract predicates
+            predicateStart = path.indexOf(QuerySpecification.PREDICATE_START, predicateStart + 1);
+
+            // any predicates in this node?
+            if (inx != -1 && (predicateStart == -1 || predicateStart > inx))
+            {
+                // no
+                node = path.substring(0, inx).trim();
+                path = path.substring(inx + 1).trim();
+            }
+            else if (predicateStart == -1)
+            {
+                // no and it's the last node
+                node = path;
+                path = "";
+            }
+            else
+            {
+                // yes
+                node = path.substring(0, predicateStart).trim();
+                path = path.substring(predicateStart);
+                predicateStart = 0;
+
+                while (predicateStart == 0)
+                {
+                    predicateEnd = path.indexOf(QuerySpecification.PREDICATE_END,
+                            predicateStart);
+
+                    if (predicateEnd == -1)
+                    {
+                        log.warn("useNestedStatements(): ");
+                        log.warn("    Invalid path: " + pathexpr);
+                        return "";
+                    }
+
+                    predicates.add(path.substring(1, predicateEnd).trim());
+                    path = path.substring(predicateEnd + 1).trim();
+                    inx = path.indexOf('/');
+                    predicateStart = path.indexOf(QuerySpecification.PREDICATE_START);
+                }
+
+                if (inx == 0)
+                    path = path.substring(1).trim();
+                else if (!path.equals(""))
+                {
+                    log.warn("useNestedStatements(): ");
+                    log.warn("    Invalid path: " + pathexpr);
+                    return "";
+                }
+            }
+
+            nestedStmts.insert(0, "' ").insert(0, node).insert(0,
+                    "(SELECT nodeid FROM xml_nodes WHERE nodename LIKE '");
+
+            // for the last statement: it is without " AND parentnodeid IN "
+            if (!path.equals(""))
+                nestedStmts.insert(0, "AND parentnodeid IN ");
+
+            if (predicates.size() > 0)
+            {
+                for (int n = 0; n < predicates.size(); n++)
+                {
+                    String predSQL = predicate2SQL((String) predicates.get(n));
+
+                    if (predSQL.equals(""))
+                        return "";
+
+                    nestedStmts.append(predSQL).append(' ');
+                }
+            }
+
+            nestedStmts.append(") ");
+        }
+        while (!path.equals(""));
+
+        return nestedStmts.toString();
+    }
+
     /**
+     * 
+     */
+    public static String predicate2SQL(String predicate)
+    {
+        String path = predicate.trim();
+        int equals = path.indexOf('=');
+        String literal = null;
+
+        if (equals != -1)
+        {
+            literal = path.substring(equals + 1).trim();
+            path = path.substring(0, equals).trim();
+            int sQuote = literal.indexOf('\'');
+            int dQuote = literal.indexOf('"');
+
+            if (sQuote == -1 && dQuote == -1)
+            {
+                log.warn("predicate2SQL(): ");
+                log.warn("    Invalid or unsupported predicate: " + predicate);
+                return "";
+            }
+
+            if (sQuote == -1 &&
+                (dQuote != 0 ||
+                 literal.indexOf('"', dQuote + 1) != literal.length() - 1))
+            {
+                log.warn("predicate2SQL(): ");
+                log.warn("    Invalid or unsupported predicate: " + predicate);
+                return "";
+            }
+
+            if (sQuote != 0 ||
+                literal.indexOf('\'', sQuote + 1) != literal.length() - 1)
+            {
+                log.warn("predicate2SQL(): ");
+                log.warn("    Invalid or unsupported predicate: " + predicate);
+                return "";
+            }
+        }
+
+        StringBuffer sql = new StringBuffer();
+        int attribute = path.indexOf('@');
+
+        if (attribute == -1)
+        {
+            if (literal != null)
+            {
+                sql.append("AND nodeid IN (SELECT parentnodeid FROM xml_nodes WHERE nodetype = 'TEXT' AND nodedata LIKE ")
+                    .append(literal).append(")");
+            }
+        }
+        else
+        {
+            sql.append(
+                    "AND nodeid IN (SELECT parentnodeid FROM xml_nodes WHERE nodetype = 'ATTRIBUTE' AND nodename LIKE '")
+                    .append(path.substring(attribute + 1).trim()).append("' ");
+
+            if (literal != null)
+            {
+                sql.append("AND nodedata LIKE ").append(literal);
+            }
+
+            sql.append(")");
+            path = path.substring(0, attribute).trim();
+
+            if (path.endsWith("/"))
+                path = path.substring(0, path.length() - 1).trim();
+            else
+            {
+                if (!path.equals(""))
+                {
+                    log.warn("predicate2SQL(): ");
+                    log.warn("    Invalid or unsupported predicate: " + predicate);
+                    return "";
+                }
+            }
+        }
+
+        while (!path.equals(""))
+        {
+            int ndx = path.lastIndexOf('/');
+            int predicateEnd = -1;
+            int predicateStart;
+            String node;
+
+            if (ndx != -1)
+            {
+                node = path.substring(ndx + 1).trim();
+                path = path.substring(0, ndx).trim();
+            }
+            else
+            {
+                node = path;
+                path = "";
+            }
+
+            if (!node.equals(""))
+                sql.insert(0, "' ").insert(0, node)
+                    .insert(0, "(SELECT parentnodeid FROM xml_nodes WHERE nodename LIKE '").append(") ");
+            else if (!path.equals(""))
+            {
+                log.warn("predicate2SQL(): ");
+                log.warn("    Invalid or unsupported predicate: " + predicate);
+                return "";
+            }
+
+            if (path.equals(""))
+            {
+                sql.insert(0,
+                        node.equals("") ? "AND rootnodeid IN " : "AND nodeid IN ");
+            }
+            else
+            {
+                sql.append("AND nodeid IN ");
+            }
+        }
+
+        return sql.toString();
+    }
+
+    /**
      * create a String description of the query that this instance represents.
      * This should become a way to get the XML serialization of the query.
      */
