001/**
002 * Copyright 2016 Tampere University of Technology, Pori Department
003 * 
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 *   http://www.apache.org/licenses/LICENSE-2.0
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package service.tut.pori.apilta.alerts.datatypes;
017
018import java.io.InputStream;
019import java.util.List;
020
021import org.apache.commons.lang3.ArrayUtils;
022import org.apache.commons.lang3.StringUtils;
023import org.apache.log4j.Logger;
024
025import core.tut.pori.http.Definitions;
026import core.tut.pori.http.parameters.HTTPParameter;
027
028/**
029 * The default parser for location parameters.
030 * 
031 * The syntax being: ?location=LOCATION_PARAMETER{@value Definitions#SEPARATOR_URI_QUERY_TYPE_VALUE}LOCATION_VALUE{@value Definitions#SEPARATOR_URI_QUERY_TYPE_VALUE}LOCATION_PARAMETER{@value Definitions#SEPARATOR_URI_QUERY_TYPE_VALUE}LOCATION_VALUE
032 * 
033 * Possible LOCATION_PARAMETERs and corresponding LOCATION_VALUEs:
034 * 
035 * <ul>
036 *  <li>"{@value service.tut.pori.apilta.alerts.datatypes.LocationParameter#LOCATION_PARAMETER_COORDINATE}" and "latitude longitude". 
037 *  The datum for the coordinate depends on the service, but both latitude and longitude must be in numeric (Java double) format with decimal separator being period (.). 
038 *  Latitude and longitude must be separated by white space.</li>
039 *  <li>"{@value service.tut.pori.apilta.alerts.datatypes.LocationParameter#LOCATION_PARAMETER_HEADING}" and "heading_value". Heading value (generally, in degrees) must be a double with period (.) as decimal separator.</li>
040 * </ul>
041 * 
042 * For example, the location: ?location={@value service.tut.pori.apilta.alerts.datatypes.LocationParameter#LOCATION_PARAMETER_COORDINATE}{@value Definitions#SEPARATOR_URI_QUERY_TYPE_VALUE}0 0{@value Definitions#SEPARATOR_URI_QUERY_TYPE_VALUE}{@value service.tut.pori.apilta.alerts.datatypes.LocationParameter#LOCATION_PARAMETER_HEADING}{@value Definitions#SEPARATOR_URI_QUERY_TYPE_VALUE}0 would be location in coordinate 0.0 and the heading would be 0
043 *
044 * The parser does not validate the given values, remember to check the returned {@link #getValue()} object with {@link service.tut.pori.apilta.alerts.datatypes.Location#isValid(Location)}.
045 * Especially, the presence of heading value without coordinate values should be checked by the implementing service if checks are required.
046 * 
047 * If multiple values are given, the output is unspecified (most likely, the last value given in the query URL will be used for initialization).
048 */
049public class LocationParameter extends HTTPParameter {
050  /** location parameter for a coordinate value */
051  public static final String LOCATION_PARAMETER_COORDINATE = "coordinate";
052  /** location parameter for a heading value */
053  public static final String LOCATION_PARAMETER_HEADING = "heading";
054  /** the default HTTP parameter name */
055  public static final String PARAMETER_DEFAULT_NAME = "location";
056  private static final Logger LOGGER = Logger.getLogger(LocationParameter.class);
057  private Location _location = null;
058
059  @Override
060  public void initialize(List<String> parameterValues) throws IllegalArgumentException {
061    for(String value : parameterValues){
062      initialize(value);
063    }
064  }
065
066  @Override
067  public void initialize(String parameterValue) throws IllegalArgumentException {
068    String[] valuePairs = StringUtils.split(parameterValue, Definitions.SEPARATOR_URI_QUERY_TYPE_VALUE);
069    if(ArrayUtils.isEmpty(valuePairs) || valuePairs.length % 2 != 0){ // check that we have values and that the list contains pairs (param + value)
070      throw new IllegalArgumentException("Invalid value string: "+parameterValue);
071    }
072    
073    for(int i=0;i<valuePairs.length;++i){
074      switch(valuePairs[i]){
075        case LOCATION_PARAMETER_COORDINATE:
076          parseCoordinate(valuePairs[++i]);
077          break;
078        case LOCATION_PARAMETER_HEADING:
079          parseHeading(valuePairs[++i]); // the next string is the value parameter
080          break;
081        default:
082          throw new IllegalArgumentException("Invalid value string: "+parameterValue);
083      }
084    }
085  }
086  
087  /**
088   * 
089   * @param coordinate in the format LATITUDE LONGITUDE, with latitude and longitude fitting in double and being separated by a whitespace
090   * @throws IllegalArgumentException
091   */
092  private void parseCoordinate(String coordinate) throws IllegalArgumentException {
093    String[] latLon = StringUtils.split(coordinate);
094    if(ArrayUtils.isEmpty(latLon) || latLon.length != 2){
095      throw new IllegalArgumentException("Invalid value for "+LOCATION_PARAMETER_COORDINATE+" : "+coordinate);
096    }
097    
098    Double latitude = null;
099    Double longitude = null;
100    try{
101      latitude = Double.valueOf(latLon[0]);
102      longitude = Double.valueOf(latLon[1]);
103    } catch (NumberFormatException ex){
104      LOGGER.warn(ex, ex);
105      throw new IllegalArgumentException("Invalid value for "+LOCATION_PARAMETER_COORDINATE+" : "+coordinate);
106    }
107    
108    if(_location == null){
109      _location = new Location();
110    }
111    _location.setLatitude(latitude);
112    _location.setLongitude(longitude);
113  }
114  
115  /**
116   * 
117   * @param heading as an integer value
118   * @throws IllegalArgumentException
119   */
120  private void parseHeading(String heading) throws IllegalArgumentException {
121    Double h = null;
122    try{
123      h = Double.valueOf(heading);
124    } catch (NumberFormatException ex){
125      LOGGER.warn(ex, ex);
126      throw new IllegalArgumentException("Invalid value for "+LOCATION_PARAMETER_HEADING+" : "+heading);
127    }
128    
129    if(_location == null){
130      _location = new Location();
131    }
132    _location.setHeading(h);
133  }
134
135  @Override
136  public boolean hasValues() {
137    return (_location != null);
138  }
139
140  @Override
141  public Location getValue() {
142    return _location;
143  }
144  
145  @Override
146  public void initialize(InputStream parameterValue) throws UnsupportedOperationException {
147    throw new UnsupportedOperationException("The use of HTTP Body is not implemented for this parameter.");
148  }
149}