/* * Copyright (c) 2013-2015 Ashutosh Kumar Singh * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package net.aksingh.owmjapis; import org.json.JSONArray; import org.json.JSONObject; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; /** *

* Provides default behaviours and implementations for: * 1. {@link net.aksingh.owmjapis.CurrentWeather} * It defines common methods like has, get and some others. *

* * @author Ashutosh Kumar Singh * @version 2014/12/21 * @since 2.5.0.1 */ public abstract class AbstractWeather extends AbstractResponse { /* JSON Keys */ static final String JSON_CLOUDS = "clouds"; static final String JSON_COORD = "coord"; static final String JSON_MAIN = "main"; static final String JSON_WIND = "wind"; private static final String JSON_WEATHER = "weather"; private static final String JSON_DATE_TIME = "dt"; /* Instance variables */ private final Date dateTime; private final int weatherCount; private final List weatherList; /* Constructors */ AbstractWeather() { super(); this.weatherCount = 0; this.weatherList = null; this.dateTime = null; } AbstractWeather(JSONObject jsonObj) { super(jsonObj); long sec = (jsonObj != null) ? jsonObj.optLong(JSON_DATE_TIME, Long.MIN_VALUE) : Long.MIN_VALUE; if (sec != Long.MIN_VALUE) { // converting seconds to Date object /* Bugfix: It always return "Sat Jan 17 04:10:42 CET 1970" Issue: #3 at http://code.aksingh.net/owm-japis/issue/3/problem-with-datetime Incorrect: this.dateTime = new Date(sec); Correct: this.dateTime = new Date(sec * 1000); Reason: Date requires milliseconds but previously, seconds were provided. */ this.dateTime = new Date(sec * 1000); } else { this.dateTime = null; } JSONArray weatherArray = (jsonObj != null) ? jsonObj.optJSONArray(JSON_WEATHER) : new JSONArray(); this.weatherList = (weatherArray != null) ? new ArrayList(weatherArray.length()) : Collections.EMPTY_LIST; if (this.weatherList != Collections.EMPTY_LIST) { for (int i = 0; i < weatherArray.length(); i++) { JSONObject weatherObj = weatherArray.optJSONObject(i); if (weatherObj != null) { this.weatherList.add(new Weather(weatherObj)); } } } this.weatherCount = this.weatherList.size(); } /** * @return true if date/time is available, otherwise false. */ public boolean hasDateTime() { return (this.dateTime != null); } /** * @return true if Weather instance(s) is available, otherwise false. */ public boolean hasWeatherInstance() { return (weatherCount != 0); } /** * @return Date and time if available, otherwise null. */ public Date getDateTime() { return this.dateTime; } /** * @return Count of Weather instance(s) if available, otherwise 0. */ public int getWeatherCount() { return this.weatherCount; } /** * @param index Index of Weather instance in the list. * @return Weather instance if available, otherwise null. */ public Weather getWeatherInstance(int index) { return this.weatherList.get(index); } /** *

* Provides default behaviours for Cloud *

* * @author Ashutosh Kumar Singh * @version 2013/12/23 * @since 2.5.0.1 */ abstract public static class Clouds implements Serializable { private static final String JSON_CLOUDS_ALL = "all"; private final float percentOfClouds; Clouds() { this.percentOfClouds = Float.NaN; } Clouds(JSONObject jsonObj) { this.percentOfClouds = (float) jsonObj.optDouble(this.JSON_CLOUDS_ALL, Double.NaN); } /** * Tells if percentage of clouds is available or not. * * @return true if data available, otherwise false */ public boolean hasPercentageOfClouds() { return (this.percentOfClouds != Float.NaN); } /** * @return Percentage of all clouds if available, otherwise Float.NaN. */ public float getPercentageOfClouds() { return this.percentOfClouds; } } /** *

* Provides default behaviours for Coord, i.e., coordinates. *

* * @author Ashutosh Kumar Singh * @version 2013/12/23 * @since 2.5.0.1 */ abstract public static class Coord implements Serializable { private static final String JSON_COORD_LATITUDE = "lat"; private static final String JSON_COORD_LONGITUDE = "lon"; private final float lat; private final float lon; Coord() { this.lat = Float.NaN; this.lon = Float.NaN; } Coord(JSONObject jsonObj) { this.lat = (float) jsonObj.optDouble(this.JSON_COORD_LATITUDE, Double.NaN); this.lon = (float) jsonObj.optDouble(this.JSON_COORD_LONGITUDE, Double.NaN); } /** * Tells if the latitude of the city is available or not. * * @return true if data available, otherwise false */ public boolean hasLatitude() { return (this.lat != Float.NaN); } /** * Tells if the longitude of the city is available or not. * * @return true if data available, otherwise false */ public boolean hasLongitude() { return (this.lon != Float.NaN); } /** * @return Latitude of the city if available, otherwise Float.NaN. */ public float getLatitude() { return this.lat; } /** * @return Longitude of the city if available, otherwise Float.NaN. */ public float getLongitude() { return this.lon; } } /** *

* Provides default behaviours for Main, i.e., main weather elements like temperature, humidity, etc. *

* * @author Ashutosh Kumar Singh * @version 2013/12/23 * @since 2.5.0.1 */ abstract public static class Main implements Serializable { private static final String JSON_MAIN_TEMP = "temp"; private static final String JSON_MAIN_TEMP_MIN = "temp_min"; private static final String JSON_MAIN_TEMP_MAX = "temp_max"; private static final String JSON_MAIN_PRESSURE = "pressure"; private static final String JSON_MAIN_HUMIDITY = "humidity"; private final float temp; private final float minTemp; private final float maxTemp; private final float pressure; private final float humidity; Main() { this.temp = Float.NaN; this.minTemp = Float.NaN; this.maxTemp = Float.NaN; this.pressure = Float.NaN; this.humidity = Float.NaN; } Main(JSONObject jsonObj) { this.temp = (float) jsonObj.optDouble(this.JSON_MAIN_TEMP, Double.NaN); this.minTemp = (float) jsonObj.optDouble(this.JSON_MAIN_TEMP_MIN, Double.NaN); this.maxTemp = (float) jsonObj.optDouble(this.JSON_MAIN_TEMP_MAX, Double.NaN); this.pressure = (float) jsonObj.optDouble(this.JSON_MAIN_PRESSURE, Double.NaN); this.humidity = (float) jsonObj.optDouble(this.JSON_MAIN_HUMIDITY, Double.NaN); } /** * Tells if the temperature of the city is available or not. * * @return true if data available, otherwise false */ public boolean hasTemperature() { return (this.temp != Float.NaN); } /** * Tells if the minimum temperature of the city is available or not. * * @return true if data available, otherwise false */ public boolean hasMinTemperature() { return (this.minTemp != Float.NaN); } /** * Tells if the maximum temperature of the city is available or not. * * @return true if data available, otherwise false */ public boolean hasMaxTemperature() { return (this.maxTemp != Float.NaN); } /** * Tells if pressure of the city is available or not. * * @return true if data available, otherwise false */ public boolean hasPressure() { return (this.pressure != Float.NaN); } /** * Tells if humidity of the city is available or not. * * @return true if data available, otherwise false */ public boolean hasHumidity() { return (this.humidity != Float.NaN); } /** * @return Temperature of the city if available, otherwise Float.NaN. */ public float getTemperature() { return this.temp; } /** * @return Minimum temperature of the city if available, otherwise Float.NaN. */ public float getMinTemperature() { return this.minTemp; } /** * @return Maximum temperature of the city if available, otherwise Float.NaN. */ public float getMaxTemperature() { return this.maxTemp; } /** * @return Pressure of the city if available, otherwise Float.NaN. */ public float getPressure() { return this.pressure; } /** * @return Humidity of the city if available, otherwise Float.NaN. */ public float getHumidity() { return this.humidity; } } /** *

* Parses weather data and provides methods to get/access the same information. * This class provides has and get methods to access the information. *

*

* has methods can be used to check if the data exists, i.e., if the data was available * (successfully downloaded) and was parsed correctly. * get methods can be used to access the data, if the data exists, otherwise get * methods will give value as per following basis: * Boolean: false * Integral: Minimum value (MIN_VALUE) * Floating point: Not a number (NaN) * Others: null *

* * @author Ashutosh Kumar Singh * @version 2014/12/27 * @since 2.5.0.3 */ public static class Weather implements Serializable { private static final String JSON_WEATHER_ID = "id"; private static final String JSON_WEATHER_MAIN = "main"; private static final String JSON_WEATHER_DESCRIPTION = "description"; private static final String JSON_WEATHER_ICON = "icon"; private final int id; private final String name; private final String description; private final String icon; Weather() { this.id = Integer.MIN_VALUE; this.name = null; this.description = null; this.icon = null; } Weather(JSONObject jsonObj) { this.id = jsonObj.optInt(this.JSON_WEATHER_ID, Integer.MIN_VALUE); this.name = jsonObj.optString(this.JSON_WEATHER_MAIN, null); this.description = jsonObj.optString(this.JSON_WEATHER_DESCRIPTION, null); this.icon = jsonObj.optString(this.JSON_WEATHER_ICON, null); } /** * Tells if weather's code is available or not. * * @return true if data available, otherwise false. */ public boolean hasWeatherCode() { return (this.id != Integer.MIN_VALUE); } /** * Tells if weather's name is available or not. * * @return true if data available, otherwise false. */ public boolean hasWeatherName() { return (this.name != null && (!this.name.equals(""))); } /** * Tells if weather's description is available or not. * * @return true if data available, otherwise false. */ public boolean hasWeatherDescription() { return (this.description != null && (!this.description.equals(""))); } /** * Tells if name of weather's icon is available or not. * * @return true if data available, otherwise false. */ public boolean hasWeatherIconName() { return (this.icon != null && (!this.icon.equals(""))); } /** * @return Code for weather of the city if available, otherwise Integer.MIN_VALUE. */ public int getWeatherCode() { return this.id; } /** * @return Name for weather of the city if available, otherwise null. */ public String getWeatherName() { return this.name; } /** * @return Description for weather of the city if available, otherwise null. */ public String getWeatherDescription() { return this.description; } /** * @return Name of icon for weather of the city if available, otherwise null. */ public String getWeatherIconName() { return this.icon; } } /** *

* Provides default behaviours for Wind. *

* * @author Ashutosh Kumar Singh * @version 2013/12/23 * @since 2.5.0.1 */ abstract public static class Wind implements Serializable { private static final String JSON_WIND_SPEED = "speed"; private static final String JSON_WIND_DEGREE = "deg"; private final float speed; private final float degree; Wind() { this.speed = Float.NaN; this.degree = Float.NaN; } Wind(JSONObject jsonObj) { this.speed = (float) jsonObj.optDouble(this.JSON_WIND_SPEED, Double.NaN); this.degree = (float) jsonObj.optDouble(this.JSON_WIND_DEGREE, Double.NaN); } /** * Tells if speed of wind in the city is available or not. * * @return true if data available, otherwise false. */ public boolean hasWindSpeed() { return (this.speed != Float.NaN); } /** * Tells if degree (degree gives direction) of wind in the city is available or not. * * @return true if data available, otherwise false. */ public boolean hasWindDegree() { return (this.hasWindSpeed() && (this.degree != Float.NaN)); } /** * @return Speed of wind in the city if available, otherwise Float.NaN. */ public float getWindSpeed() { return this.speed; } /** * @return Degree of wind in the city if available, otherwise Float.NaN. */ public float getWindDegree() { return this.degree; } } }