package org.intermine.webservice.server.query.result;

/*
 * Copyright (C) 2002-2011 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.io.StringReader;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import org.intermine.api.profile.BagState;
import org.intermine.api.profile.InterMineBag;
import org.intermine.pathquery.PathQuery;
import org.intermine.pathquery.PathQueryBinding;
import org.intermine.webservice.server.exceptions.BadRequestException;
import org.intermine.webservice.server.exceptions.InternalErrorException;


/**
 * PathQueryBuilder builds PathQuery object from xml and validates it.
 *
 * @author Jakub Kulaviak
 **/
public class PathQueryBuilder
{

    private PathQuery pathQuery;

    private static Logger logger = Logger.getLogger(PathQueryBuilder.class);

    /**
     * Constructor for testing.
     */
    PathQueryBuilder() {
        // empty constructor for testing
    }

    /**
     * PathQueryBuilder constructor.
     * @param xml xml string from which will be PathQuery constructed
     * @param schemaUrl url of XML Schema file, validation is performed according this file
     * @param savedBags previously saved bags
     */
    public PathQueryBuilder(String xml, String schemaUrl, Map<String, InterMineBag> savedBags) {
        buildQuery(xml, schemaUrl, savedBags);
    }

    /**
     * Perform the build operation.
     * @param xml xml string from which will be PathQuery constructed
     * @param schemaUrl url of XML Schema file, validation is performed according this file
     * @param savedBags previously saved bags.
     */
    void buildQuery(String xml, String schemaUrl,
            Map<String, InterMineBag> savedBags) {
        XMLValidator validator = new XMLValidator();
        validator.validate(xml, schemaUrl);
        if (validator.getErrorsAndWarnings().size() == 0) {
            pathQuery = PathQueryBinding.unmarshalPathQuery(new StringReader(xml),
                    PathQuery.USERPROFILE_VERSION);

            if (!pathQuery.isValid()) {
                throw new BadRequestException("XML is well formatted but query contains errors: "
                        + formatMessage(pathQuery.verifyQuery()));
            }

            // check bags used by this query exist and are current
            Set<String> missingBags = new HashSet<String>();
            Set<String> toUpgrade = new HashSet<String>();
            for (String bagName : pathQuery.getBagNames()) {
                if (!savedBags.containsKey(bagName)) {
                    missingBags.add(bagName);
                } else {
                    InterMineBag bag = savedBags.get(bagName);
                    if (BagState.CURRENT != BagState.valueOf(bag.getState())) {
                        toUpgrade.add(bagName);
                    }
                }
            }
            if (!missingBags.isEmpty()) {
                throw new BadRequestException(
                        "The query XML is well formatted but you do not have access to the "
                        + "following mentioned lists: " + missingBags
                        + " query: " + xml);
            }
            if (!toUpgrade.isEmpty()) {
                throw new InternalErrorException(
                        "The query XML is well formatted, but the following lists"
                        + " are not 'current', and need to be manually upgraded: " + toUpgrade);
            }
        } else {
            logger.debug("Received invalid xml: " + xml);
            throw new BadRequestException(formatMessage(validator.getErrorsAndWarnings()));
        }
    }

    private String formatMessage(List<String> msgs) {
        StringBuilder sb = new StringBuilder();
        for (String msg : msgs) {
            sb.append(msg);
            if (!msg.endsWith(".")) {
                sb.append(".");
            }
        }
        return sb.toString();
    }

    /**
     * Returns parsed PathQuery.
     * @return parsed PathQuery
     */
    public PathQuery getQuery() {
        return pathQuery;
    }
}

@@ -424,16 +424,45 @@
 l.List;%0A
+import java.util.Collection;%0A
 import j
@@ -2436,17 +2436,18 @@
  errors:
- 
+%5Cn
 %22%0A      
@@ -3380,24 +3380,11 @@
 sts:
- %22 + missingBags
+%5Cn%22
 %0A   
@@ -3410,24 +3410,34 @@
   + 
-%22 query: %22 + xml
+formatMessage(missingBags)
 );%0A 
@@ -3702,21 +3702,60 @@
 pgraded:
- %22 + 
+%5Cn%22%0A                        + formatMessage(
 toUpgrad
@@ -3756,16 +3756,17 @@
 Upgrade)
+)
 ;%0A      
@@ -3890,16 +3890,79 @@
 ception(
+%22Query does not pass XML validation:%5Cn%22 %0A                    + 
 formatMe
@@ -4053,20 +4053,26 @@
 Message(
-List
+Collection
 %3CString%3E
@@ -4266,32 +4266,61 @@
 ;%0A            %7D%0A
+            sb.append(%22%5Cn%22);%0A
         %7D%0A      

Better formatted error messages