org.intermine.webservice.server.query.QueryRequestParser.java Source code

Java tutorial

Introduction

Here is the source code for org.intermine.webservice.server.query.QueryRequestParser.java

Source

package org.intermine.webservice.server.query;

/*
 * Copyright (C) 2002-2013 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.UnsupportedEncodingException;

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import org.intermine.api.query.KeyFormatException;
import org.intermine.api.query.NotPresentException;
import org.intermine.api.query.QueryStore;
import org.intermine.api.query.QueryStoreException;
import org.intermine.webservice.server.WebServiceRequestParser;
import org.intermine.webservice.server.exceptions.BadRequestException;

/**
 * Processes query request parameters. The main function of this
 * abstraction to to ensure the Query-xml is properly
 * decoded.
 *
 * @author Alex Kalderimis
 **/
public class QueryRequestParser extends WebServiceRequestParser {
    private static Logger logger = Logger.getLogger(QueryRequestParser.class);

    protected HttpServletRequest request;

    private QueryStore queryStore;

    /**
     * RequestProcessor constructor.
     * @param request request
     */
    public QueryRequestParser(QueryStore queryStore, HttpServletRequest request) {
        this.queryStore = queryStore;
        this.request = request;
    }

    private static final String QUERY_PARAMETER = "query";

    /**
     * The query parameter for queries passed as a
     * lzw compressed string.
     */
    private static final String QLZW_PARAMETER = "qlzw";

    private static final String QID = "qid";

    /**
     * Function for dealing with encoding issues with various
     * inputs.
     * @param latin1 XML in latin1 encoding.
     * @return The fixed string.
     */
    private static String fixEncoding(String latin1) {
        try {
            byte[] bytes = latin1.getBytes("ISO-8859-1");
            if (!validUTF8(bytes)) {
                return latin1;
            }
            return new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // Impossible, throw unchecked
            throw new IllegalStateException("Neither Latin1 nor UTF-8: " + e.getMessage());
        }

    }

    private static boolean validUTF8(byte[] input) {
        int i = 0;
        // Check for BOM
        if (input.length >= 3 && (input[0] & 0xFF) == 0xEF
                && (input[1] & 0xFF) == 0xBB & (input[2] & 0xFF) == 0xBF) {
            i = 3;
        }

        int end;
        for (int j = input.length; i < j; ++i) {
            int octet = input[i];
            if ((octet & 0x80) == 0) {
                continue; // ASCII
            }

            // Check for UTF-8 leading byte
            if ((octet & 0xE0) == 0xC0) {
                end = i + 1;
            } else if ((octet & 0xF0) == 0xE0) {
                end = i + 2;
            } else if ((octet & 0xF8) == 0xF0) {
                end = i + 3;
            } else {
                // Java only supports BMP so 3 is max
                return false;
            }

            while (i < end) {
                i++;
                octet = input[i];
                if ((octet & 0xC0) != 0x80) {
                    // Not a valid trailing byte
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Decompress a list of output ks to a string.
     *
     * Gratefully nicked from Stack-Overflow.
     **/
    public static String decompressLZW(List<Integer> compressed) {
        // Build the dictionary.
        int dictSize = 256;
        Map<Integer, String> dictionary = new HashMap<Integer, String>();
        for (int i = 0; i < 256; i++)
            dictionary.put(i, "" + (char) i);

        String w = "" + (char) (int) compressed.remove(0);
        String result = w;
        for (int k : compressed) {
            String entry;
            if (dictionary.containsKey(k))
                entry = dictionary.get(k);
            else if (k == dictSize)
                entry = w + w.charAt(0);
            else
                throw new IllegalArgumentException("Bad compressed k: " + k);

            result += entry;

            // Add w+entry[0] to the dictionary.
            dictionary.put(dictSize++, w + entry.charAt(0));

            w = entry;
        }
        return result;
    }

    /**
     * Take in a LZW encoded string and return a decoded plain-text string.
     */
    public static String decodeLZWString(String encoded) {
        List<Integer> codes = new ArrayList<Integer>();
        encoded = fixEncoding(encoded);
        int length = encoded.length();
        for (int i = 0; i < length; i++) {
            Integer cp = Integer.valueOf(encoded.codePointAt(i));
            codes.add(cp);
        }
        return decompressLZW(codes);
    }

    /**
     * Get query XML from a request.
     * @param req The request to get the XML from.
     * @return The XML string version of the query, in the correct encoding.
     */
    public String getQueryXml() {
        String xmlQuery, lzwQuery, qid;
        qid = request.getParameter(QID);
        xmlQuery = request.getParameter(QUERY_PARAMETER);
        lzwQuery = request.getParameter(QLZW_PARAMETER);

        if (StringUtils.isNotBlank(qid)) {
            try {
                return queryStore.getQuery(qid);
            } catch (QueryStoreException e) {
                throw new BadRequestException(e.getMessage());
            }
        } else if (StringUtils.isNotBlank(lzwQuery)) {
            xmlQuery = decodeLZWString(lzwQuery);
        }
        if (StringUtils.isBlank(xmlQuery)) {
            throw new BadRequestException("The 'query' parameter must not be blank");
        }
        return fixEncoding(xmlQuery);
    }
}