content: /**
   * ﻿Copyright (C) 2007 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
   * Public License for more details.
   */
  package org.n52.wps.server.request;
  
  import java.util.UUID;
  import java.util.concurrent.Callable;
  
  import org.apache.commons.collections.map.CaseInsensitiveMap;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.n52.wps.server.ExceptionReport;
  import org.n52.wps.server.response.Response;
  import org.w3c.dom.Document;
  
  /**
   * The client requests some operation from the server.
   * The request generates a unique reference based on the client, time and a count.
   * Not secure! Upcoming references are easily guessed or altered.
   * @see java.rmi.server.UID
   */
  abstract public class Request implements Callable <Response> {
  
  	protected CaseInsensitiveMap map = null;
  	protected Document doc = null;
  	protected static Logger LOGGER = LoggerFactory.getLogger(Request.class);
  	protected UUID id = null;
  	public static final String SUPPORTED_VERSION = "1.0.0";
  	public static final String[] SUPPORTED_LANGUAGES = new String[]{"en-US"};
  	
  	/**
  	 * Create a Request based on a CaseInsensitiveMap as input (HTTP GET)
  	 * @param map The Map which holds the client input.
  	 */
  	public Request(CaseInsensitiveMap map) throws ExceptionReport{
  		super();
  		this.map = map;
  	}
  	
  	/**
  	 * Create a Request based on a Document as input (HTTP POST)
  	 * @param doc The Document which holds the client input.
  	 */	
  	public Request(Document doc) throws ExceptionReport{
  		super();
  		this.doc = doc;
  	}
  	
  	/**
  	 * Returns the user input in Document form
  	 * @return Document || null if Request(Map, outputstream) was used
  	 */
  	public Document getDocument(){
  		return doc;
  	}
  	
  	/**
  	 * Returns the user input in Map form
  	 * @return Map || null if Request(Document, OutputStream) was used
  	 */	
  	public CaseInsensitiveMap getMap(){
  		return map;
  	}
  	
  	/**
  	 * Retrieve a value from an input-map with a lookup-key
  	 * @param key The lookup-key
  	 * @param map The input-map to look in
  	 * @param required If the key-value pair must be in the map.
  	 * @return The value of the key-value pair
  	 */
  	public static String getMapValue(String key, CaseInsensitiveMap map, boolean required) throws ExceptionReport{
  		if(map.containsKey(key)){
  			return ((String[]) map.get(key))[0];
  		}else if(!required){
  			LOGGER.warn("Parameter <" + key + "> not found.");
  			return null;
  		}else{
  			//Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904
  			throw new ExceptionReport("Parameter <" + key + "> not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, key);
  		}
  	}
  	
  	/**
  	 * Retrieve a value from an input-map with a lookup-key
  	 * @param key The lookup-key
  	 * @param map The input-map to look in
  	 * @param required If the key-value pair must be in the map.
  	 * @return The value of the key-value pair
  	 */
  	public static String getMapValue(String key, CaseInsensitiveMap map, boolean required, String[] supportedValues) throws ExceptionReport{
  		if(map.containsKey(key)){
  			
  			String value = ((String[]) map.get(key))[0];
  			
  			for (String string : supportedValues) {
  				if(string.equalsIgnoreCase(value)){					
  					return value;
  				}
  			}
  			throw new ExceptionReport("Invalid value for parameter <" + key + ">.", ExceptionReport.INVALID_PARAMETER_VALUE, key);
  		}else if(!required){
  			LOGGER.warn("Parameter <" + key + "> not found.");
  			return null;
  		}else{
  			//Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904
  			throw new ExceptionReport("Parameter <" + key + "> not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, key);
  		}
  	}
  
  	/**
  	 * Retrieve an array of values from an input-map with a lookup-key
  	 * @param key The lookup-key
  	 * @param map The input-map to look in
  	 * @param required If the key-value pair must be in the map.
  	 * @return The array of values of the key-value pair
  	 */
  	public static String[] getMapArray(String key, CaseInsensitiveMap map, boolean required) throws ExceptionReport{
  		if(map.containsKey(key)){
  			return (String[]) map.get(key);
  		}else if(!required){
  			LOGGER.warn("Parameter <" + key + "> not found.");
  			return null;
  		}else{
  			//Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904
  			throw new ExceptionReport("Parameter <" + key + "> not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, key);
  		}
  	}
  	
  	/**
  	 * Retrieve a value from the client-input-map with a lookup-key
  	 * @param The lookup-key
  	 * @return The value of the key-value pair
  	 */
  	protected String getMapValue(String key, boolean required) throws ExceptionReport{
  		return Request.getMapValue(key, this.map, required);
  	}
  	
  	/**
  	 * Retrieve a value from the client-input-map with a lookup-key
  	 * @param The lookup-key
  	 * @return The value of the key-value pair
  	 */
  	protected String getMapValue(String key, boolean required, String[] supportedValues) throws ExceptionReport{
  		return Request.getMapValue(key, this.map, required, supportedValues);
  	}
  
  	/**
  	 * Retrieve an array of values from the client-input-map with a lookup-key
  	 * @param The lookup-key
  	 * @return The array of values of the key-value pair
  	 */
  	protected String[] getMapArray(String key, boolean required) throws ExceptionReport{
  		return Request.getMapArray(key, this.map, required);
  	}
  
  	/**
  	 * Returns the version that the client requested.
  	 * @return An array of versions that are compatible with the client 
  	 */
  	protected String[] getRequestedVersions(boolean mandatory) throws ExceptionReport{
  			return getMapArray("version", mandatory);
  		
  	}
  	
  	/**
  	 * The process (request) on the server could require a specific version on the client
  	 * @param version The version that is required on the client
  	 * @return True if the required version matches, False otherwise.
  	 */
  	public boolean requireVersion(String version, boolean mandatory) throws ExceptionReport{
  		String[] versions = getRequestedVersions(mandatory);
  		if(mandatory && versions == null) {
  			//Fix for Bug 904 https://bugzilla.52north.org/show_bug.cgi?id=904
  			throw new ExceptionReport("Parameter <version> not specified.", ExceptionReport.MISSING_PARAMETER_VALUE, "version");
  		}
  		else if(versions == null && ! mandatory) {
  			return true;
  		}
  		for(String v : versions) {
  			//remove possible blanks
  			if(v.trim().equals(version)) {
  				return true;
  			}
  		}
  		return false;
  	}
  	
  	/**
  	 * Accumulates the Strings in an array, separated by ", " (without quotes).
  	 * @param strings The array to accumulate
  	 * @return The accumulated String
  	 */
  	public static String accumulateString(String[] strings) {
  		StringBuffer sb = new StringBuffer();
  		for(int i = 0; i < strings.length; i++) {
  			String s = strings[i];
  			if(!(i == strings.length-1)){
  				sb.append(s + ", ");
  			}else{
  				sb.append(s);
  			}
  		}
  		return sb.toString();
  	}
  	
  	public UUID getUniqueId(){
  		if (id == null) {
  			this.id = UUID.randomUUID();
  		}
  		return id;
  	}
  
  	/**
  	 * Checks, if the language is supported by the WPS.
  	 * The language parameter is optional, however, if a wrong language is requested, 
  	 * an ExceptionReport has to be returned to the client.
  	 * 
  	 * See https://bugzilla.52north.org/show_bug.cgi?id=905.
  	 * 
  	 * @param language The language to be checked.
  	 * @throws ExceptionReport If a wrong language is requested, this ExceptionReport will be returned to the client.
  	 */
  	public static void checkLanguageSupported(String language) throws ExceptionReport {
  		
  		for (String supportedLanguage : SUPPORTED_LANGUAGES) {
  			if(supportedLanguage.equals(language)){
  				return;
  			}
  		}
  		throw new ExceptionReport(
  				"The requested language " + language + " is not supported",
  				ExceptionReport.INVALID_PARAMETER_VALUE, "language");
  	}
  	
  	abstract public Object getAttachedResult();
  	
  	/**
  	 * After creation a Request is handled. This is done by calling this method.
  	 * This handling could contain a lot of computations. These computations should
  	 * be called from within this method.
  	 * @return A Response to the client Request
  	 * @see java.util.concurrent.Callable#call()
  	 */
  	abstract public Response call() throws ExceptionReport;
  	
  	/**
  	 * There should be some validation required on the (input of the) clients Request.
  	 * @return True if the clients Request can be handled without problems, False otherwise
  	 */
  	abstract public boolean validate() throws ExceptionReport;
  
  }
  	