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}