From 1a09f739aec85543e1ab6a471e9f0f074ac082fb Mon Sep 17 00:00:00 2001 From: Ashutosh Kumar Singh Date: Thu, 9 Nov 2017 03:06:02 +0530 Subject: [PATCH] 2.5.1.0: Rewrote the lib. in Kotlin --- .editorconfig | 10 + .gitignore | 21 +- LICENSE.txt | 2 +- README.md | 194 +-- VERSIONS.md | 84 + build.gradle | 145 +- gradle/wrapper/gradle-wrapper.jar | Bin 51017 -> 54788 bytes gradle/wrapper/gradle-wrapper.properties | 43 +- gradlew | 74 +- gradlew.bat | 14 +- settings.gradle | 20 +- .../aksingh/owmjapis/AbstractForecast.java | 431 +++-- .../aksingh/owmjapis/AbstractResponse.java | 141 +- .../net/aksingh/owmjapis/AbstractWeather.java | 915 ++++++----- .../net/aksingh/owmjapis/CurrentWeather.java | 1019 ++++++------ .../net/aksingh/owmjapis/DailyForecast.java | 485 +++--- .../net/aksingh/owmjapis/HourlyForecast.java | 625 ++++---- .../net/aksingh/owmjapis/OpenWeatherMap.java | 1419 ++++++++--------- src/main/java/net/aksingh/owmjapis/Tools.java | 150 +- .../net/aksingh/owmjapis/api/APIException.kt | 30 + .../aksingh/owmjapis/api/CurrentWeatherAPI.kt | 67 + .../aksingh/owmjapis/api/DailyForecastAPI.kt | 53 + .../aksingh/owmjapis/api/HourlyForecastAPI.kt | 49 + .../kotlin/net/aksingh/owmjapis/core/OWM.kt | 873 ++++++++++ .../aksingh/owmjapis/model/CurrentWeather.kt | 118 ++ .../owmjapis/model/CurrentWeatherList.kt | 62 + .../aksingh/owmjapis/model/DailyForecast.kt | 70 + .../aksingh/owmjapis/model/HourlyForecast.kt | 89 ++ .../net/aksingh/owmjapis/model/param/City.kt | 59 + .../net/aksingh/owmjapis/model/param/Cloud.kt | 39 + .../net/aksingh/owmjapis/model/param/Coord.kt | 44 + .../net/aksingh/owmjapis/model/param/Data.kt | 93 ++ .../net/aksingh/owmjapis/model/param/Main.kt | 74 + .../net/aksingh/owmjapis/model/param/Rain.kt | 39 + .../net/aksingh/owmjapis/model/param/Snow.kt | 39 + .../aksingh/owmjapis/model/param/System.kt | 84 + .../net/aksingh/owmjapis/model/param/Temp.kt | 64 + .../aksingh/owmjapis/model/param/Weather.kt | 54 + .../net/aksingh/owmjapis/model/param/Wind.kt | 49 + .../aksingh/owmjapis/util/ConversionTools.kt | 110 ++ .../net/aksingh/owmjapis/util/OkHttpTools.kt | 62 + .../net/aksingh/owmjapis/util/SystemTools.kt | 76 + .../aksingh/owmjapis/CurrentWeatherTest.java | 85 +- .../aksingh/owmjapis/DailyForecastTest.java | 109 +- .../aksingh/owmjapis/HourlyForecastTest.java | 109 +- 45 files changed, 5376 insertions(+), 3016 deletions(-) create mode 100644 .editorconfig create mode 100644 VERSIONS.md create mode 100644 src/main/kotlin/net/aksingh/owmjapis/api/APIException.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/api/CurrentWeatherAPI.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/api/DailyForecastAPI.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/api/HourlyForecastAPI.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/core/OWM.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeather.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeatherList.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/DailyForecast.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/HourlyForecast.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/City.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Cloud.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Coord.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Data.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Main.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Rain.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Snow.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/System.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Temp.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Weather.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/model/param/Wind.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/util/ConversionTools.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/util/OkHttpTools.kt create mode 100644 src/main/kotlin/net/aksingh/owmjapis/util/SystemTools.kt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f09989 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitignore b/.gitignore index be43da5..d7b04e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,18 @@ +# NetBeans lib/nblibraries.properties nbproject/ -/build/ +build/ build.xml -/lib/CopyLibs/ -/dist/ -/.gradle/ -/.idea/ -owm-japis.iml \ No newline at end of file + +# Gradle +.gradle/ + +# IntelliJ IDEA +.idea/ +owm-japis.iml +lib/CopyLibs/ +dist/ +out/ + +# Custom +src/main/kotlin/net/aksingh/owmjapis/demo/ diff --git a/LICENSE.txt b/LICENSE.txt index 24a127e..731d77d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013-2015 Ashutosh Kumar Singh +Copyright (c) 2013- 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 diff --git a/README.md b/README.md index 26083ff..f836f85 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,31 @@ ####Java Library for OpenWeatherMap.org Weather APIs -OWM JAPIs lets you develop weather-aware applications for **Java and Android platforms** in minimum time using OWM JAPIs, an easy-to-use, detailed and documented weather APIs' wrapper library for retrieving weather data from OpenWeatherMap.org. You can easily **retrieve and use weather data** in your applications using this library. +OWM JAPIs lets you develop weather-aware applications for **Java, Android, and Kotlin** platforms in minimum time. It is an easy-to-use, well-documented wrapper library for OpenWeatherMap.org's Weather APIs. You can easily **retrieve and use weather data** in your applications using this library. -OWM JAPIs allows you to **fetch weather data in only 3-5 lines of code** (excluding any other/skeleton code, of course). You can develop applications and services for multiple platforms using this library, such as Windows, Mac OS X, Linux, and Android. +OWM JAPIs allows you to **fetch weather data in only 3-5 lines of code**. You can develop applications and services for multiple platforms using this library, such as Windows, Mac OS X, Linux, and Android. -Homepage: http://code.aksingh.net/owm-japis +*Homepage:* [http://go.aksingh.net/owm-japis](http://go.aksingh.net/owm-japis) ###Why to use OWM JAPIs? 1. Free 2. Easy to use -3. Minimizes your code +3. Supports re-use +4. Minimizes the code +5. Developer-friendly -OWM JAPIs lets you **focus just on your application's logic** and **weather retrieval code is provided** by this library. Additionally, weather retrieval code becomes very short using this library – as less as 3-5 lines of code can get you weather data from OpenWeatherMap.org in your Java or Android application. **Surprising, right? Have a look on the example(s) below.** +With OWM JAPIs, you can **focus just on your application's logic** and **delegate weather retrieval** to this library. As a bonus, weather retrieval code becomes very short using this library - as less as 3-5 lines of code can get you weather data from OpenWeatherMap.org in your Java, Android, or Kotlin application. + +**Surprised? Have a look on the example(s) below.** ##How to use OWM JAPIs? -Download the library's source and binaries from [OWM JAPIs Downloads][1]. +Do you use Maven, Gradle, or some another build tool? [OWM JAPIs is available in Maven Central repository](http://search.maven.org/#search%7Cga%7C1%7Cowm-japis). -Do use Maven, Gradle or an build tool? [OWM JAPIs is available in Maven Central repository][10]. +No? Not an issue. You can download the releases (source and binaries) from [OWM JAPIs Downloads](http://go.aksingh.net/owm-japis-dloads). @@ -30,92 +34,28 @@ Do use Maven, Gradle or an build tool? [OWM JAPIs is available in Maven Central ###2.5 (Compatible with OpenWeatherMap.org's API v2.5) -####2.5.0.5 (latest) +####2.5.1.0 -**Bug-fix version:** +**Major version:** -1. Fixed 1h and 3h parameter in Rain and Snow. -2. Added Snow in CurrentWeather. -3. Added proxy support to fetch data via a proxy server. - - -####2.5.0.4 - -**Bug-fix version:** - -1. Fixed lang parameter bug. -2. Library supports serialization and parcelization. -3. Uploaded the library to Maven Central repository. - - -####2.5.0.3 - -**Implemented:** - -1. Current Weather -2. Daily Forecasts -3. Hourly Forecasts - -**New Features:** - -1. Faster than ever before -2. Raw Response for Caching purposes -3. APIs' URL building using StringBuilder -4. Multi-lingual (multiple languages) support -5. Support for external/third-party HTTP libraries (like Apache's HttpComponents) -6. Units and Language enums for setting configuration easily and correctly -7. Better maintain-able source code (for developers) -8. Ported the project to Gradle (for developers) - -**Changed:** - -1. Package's name from net.aksingh.java.api.owm to net.aksingh.owmjapis -2. Class's name from CurrentWeatherData to CurrentWeather -3. Class's name from DailyForecastData to DailyForecast -4. Class's name from ForecastWeatherData to HourlyForecast -5. Some functions' name and signature - -**Apologies for making such changes, but it was required to make things simpler. Don't worry, they're not going to change again. :)** - - -####2.5.0.2 - -**Bug-fix version:** - -1. Fixed bugs which caused wrong parsing of date and time. -2. Improved code formatting and readability (for developers). - - -####2.5.0.1 - -**Implemented:** - -1. Current Weather -2. Weather Forecasts -3. Daily Forecasts -4. Wind degree to direction converter - -**Not implemented but planned:** - -1. Searching of City -2. Weather Maps -3. Country code to name converter -4. Direction code to name converter +1. Re-wrote whole lib. in Kotlin +2. Supports retrieving Current Weather, Hourly Forecast, and Daily Forecast +3. Requires min. Java 1.7 platform and supports Java, Kotlin, and Android now ##How to use OWM JAPIs? -Anyone with little coding knowledge of Java will feel at home while using this library. **Identifiers are written to be self-explanatory and APIs' documentation** is also provided. It makes the coding process very easy, even for beginners. +Anyone with little coding knowledge of Java or Kotlin will feel at home while using this library. **Identifiers are written to be self-explanatory and APIs' documentation** (java docs) is also provided along with OWM JAPIs. 1. Add this JAR file in your project's libraries: - 1. owm-japis.jar + 1. owm-japis-{VERSION}.jar 2. Write your code as such: - 1. Create and initialize object {obj1} of "OpenWeatherMap" class + 1. Create and initialize object {obj1} of "OWM" class 2. Call this object's {obj1} functions to get the desired weather data (such as current weather, daily forecast, etc.). 3. The data is returned as a new object {obj2} of a compatible class based on the type of asked/retrieved weather data (current weather data comes in a different class's object than daily forecast data). 3. Call this returned object's {obj2} functions to get the required information from the collective weather data (such as temperature, pressure, wind speed, etc.). -Kindly have a look on the example(s) below for clear understanding. +Is it hard to understand the English of programming? Well, you are not the only one. I feel the same. Kindly have a look on the example(s) below for a clear understanding. @@ -123,19 +63,17 @@ Kindly have a look on the example(s) below for clear understanding. ### Basic Example ####Sample Code - import java.io.IOException; - import java.net.MalformedURLException; - import net.aksingh.owmjapis.CurrentWeather; - import net.aksingh.owmjapis.OpenWeatherMap; - import org.json.JSONException; + import net.aksingh.owmjapis.core.OpenWeatherMap; + import net.aksingh.owmjapis.api.APIException; + import net.aksingh.owmjapis.model.CurrentWeather; public class OwmJapisExample1 { public static void main(String[] args) - throws IOException, MalformedURLException, JSONException { + throws APIException { - // declaring object of "OpenWeatherMap" class - OpenWeatherMap owm = new OpenWeatherMap(""); + // declaring object of "OWM" class + OWM owm = new OWM("YOUR-API-KEY-HERE"); // getting current weather data for the "London" city CurrentWeather cwd = owm.currentWeatherByCityName("London"); @@ -144,57 +82,55 @@ Kindly have a look on the example(s) below for clear understanding. System.out.println("City: " + cwd.getCityName()); // printing the max./min. temperature - System.out.println("Temperature: " + cwd.getMainInstance().getMaxTemperature() - + "/" + cwd.getMainInstance().getMinTemperature() + "\'F"); + System.out.println("Temperature: " + cwd.getMainData().getTempMax() + + "/" + cwd.getMainData().getTempMin() + "\'K"); } } ####Output City: London - Temperature: 73.4/68.72 'F + Temperature: 73.4/68.72 'K ###Advance Example -You can simply use the APIs (as given in basic example) for learning, testing or experimenting with the functions provided in this library. But it may not be good enough for production or deployment environment. +You can simply use the APIs (as given in the basic example) for learning, testing or experimenting with the functions provided in this library. But it may not be good enough for production or deployment environment. Don't you agree? -Professionally, you should always **write code which can handle errors/exceptions** at the runtime. OWM JAPIs also helps here – by providing checker functions which allows you to **check if a data is available or not**, i.e., that particular data is retrieved and parsed properly or not. Of course, exception handling can still be used, but these functions are really useful and make the retrieved-data-error-handling task very simple. +Professionally, you should always **write code which can handle errors** as best as possible. OWM JAPIs helps here too by providing checker functions which allows you to **check if a data is available or not**, i.e., that particular data is retrieved and parsed properly or not. Of course, exception handling can still be used, but these functions are really useful and make the retrieved-data-error-handling task very simple. -Using OWM JAPIs, you can always check if a particular data is available or not. This is done by using the **has()** functions. For example, **hasResponseCode()** function checks if the retrieved data has a response code or not; and if available, response code can be used to check if the whole data was downloaded and parsed correctly or not. +Using OWM JAPIs, you can always check if a particular data is available or not. This is done by using the **has{DATA-NAME}()** functions. For example, **hasRespCode()** function checks if the retrieved data has a response code or not. And if available, response code can be used to check if the whole data was downloaded and parsed correctly or not, as you can see in the example below. ####Sample Code - import java.io.IOException; - import java.net.MalformedURLException; - import net.aksingh.owmjapis.CurrentWeather; - import net.aksingh.owmjapis.OpenWeatherMap; - import org.json.JSONException; + import net.aksingh.owmjapis.core.OpenWeatherMap; + import net.aksingh.owmjapis.api.APIException; + import net.aksingh.owmjapis.model.CurrentWeather; public class OwmJapisExample2 { public static void main(String[] args) - throws IOException, MalformedURLException, JSONException { - - // declaring object of "OpenWeatherMap" class - OpenWeatherMap owm = new OpenWeatherMap(""); + throws APIException { + + // declaring object of "OWM" class + OWM owm = new OWM("YOUR-API-KEY-HERE"); // getting current weather data for the "London" city CurrentWeather cwd = owm.currentWeatherByCityName("London"); - + // checking data retrieval was successful or not - if (cwd.isValid()) { - + if (cwd.hasRespCode() && cwd.getRespCode() == 200) { + // checking if city name is available if (cwd.hasCityName()) { //printing city name from the retrieved data System.out.println("City: " + cwd.getCityName()); } - + // checking if max. temp. and min. temp. is available - if (cwd.getMainInstance().hasMaxTemperature() && cwd.getMainInstance().hasMinTemperature()) { + if (cwd.hasMainData() && cwd.getMainData().hasTempMax() && cwd.getMainData().hasTempMin()) { // printing the max./min. temperature - System.out.println("Temperature: " + cwd.getMainInstance().getMaxTemperature() - + "/" + cwd.getMainInstance().getMinTemperature() + "\'F"); + System.out.println("Temperature: " + cwd.getMainData().getTempMax() + + "/" + cwd.getMainData().getTempMin() + "\'K"); } } } @@ -203,55 +139,41 @@ Using OWM JAPIs, you can always check if a particular data is available or not. ####Output City: London - Temperature: 73.4/68.72 'F + Temperature: 73.4/68.72 'K ##Source code -Download the library's source code from [OWM JAPIs Source][2]. +Download the library's source code from [OWM JAPIs Source](https://code.aksingh.net/owm-japis/src). ##Bugs / Requests Got a problem, error or bug in the library? Or want a new feature that's not already available in OWM JAPIs? -Kindly post bugs or feature requests at [OWM JAPIs Issues][3] and I will try to solve/add it in the next release. +Kindly post bugs or feature requests at [OWM JAPIs Issues](https://code.aksingh.net/owm-japis/issues) and I will try to solve/add it in the next release. ##Developer -**Ashutosh Kumar Singh** | [AKSingh.net][4] | [me@aksingh.net][9] +**Ashutosh Kumar Singh** | [www.aksingh.net](https://www.aksingh.net/) | [ashutosh@aksingh.net](mailto:ashutosh@aksingh.net) +and contributors. Do you wish to contribute? Just fork this repo on GitHub or BitBucket and send a pull request. ##Credits -1. [OpenWeatherMap.org][5] -for providing free weather data and creating easy-to-use web APIs. +1. [OpenWeatherMap.org](https://openweathermap.org/) +for providing free weather data and creating easy-to-use APIs. -2. [JSON.org][6] -for providing such a great data interchange language and its library in Java. - -3. [ForecastIO-Lib-Java][8] +2. [ForecastIO-Lib-Java](https://github.com/dvdme/forecastio-lib-java) for providing ideas like support for third-party Http libraries. -4. [Bug Reporters][3] -for reporting bugs, and even finding and sharing possible solutions for them. +3. [You, for supporting OWM JAPIs](https://code.aksingh.net/owm-japis/issues) +and for reporting bugs, and even finding and sharing possible solutions for them. ##License -Copyright (c) 2013-2014 Ashutosh Kumar Singh `` +Copyright (c) 2013-2017 Ashutosh Kumar Singh `` -Released under the terms of the [MIT license][7]. It's open source and developer-friendly. - - - [1]: http://code.aksingh.net/owm-japis/downloads - [2]: http://code.aksingh.net/owm-japis/src - [3]: http://code.aksingh.net/owm-japis/issues - [4]: http://www.aksingh.net/ - [5]: http://openweathermap.org/ - [6]: http://www.json.org/java/index.html - [7]: http://opensource.org/licenses/MIT - [8]: https://github.com/dvdme/forecastio-lib-java - [9]: mailto:me@aksingh.net - [10]: http://search.maven.org/#search%7Cga%7C1%7Cowm-japis +Released under the terms of the [MIT license](https://opensource.org/licenses/MIT). It's open source and developer-friendly. diff --git a/VERSIONS.md b/VERSIONS.md new file mode 100644 index 0000000..0057496 --- /dev/null +++ b/VERSIONS.md @@ -0,0 +1,84 @@ +##Versions +###2.5 (Compatible with OpenWeatherMap.org's API v2.5) + + +####2.5.1.0 + +**Major version:** + +1. Re-wrote whole lib. in Kotlin +2. Supports retrieving Current Weather, Hourly Forecast, and Daily Forecast +3. Requires min. Java 1.7 platform and supports Java, Kotlin, and Android now + + +####2.5.0.5 + +**Bug-fix version:** + +1. Fixed 1h and 3h parameter in Rain and Snow. +2. Added Snow in CurrentWeather. +3. Added proxy support to fetch data via a proxy server. + + +####2.5.0.4 + +**Bug-fix version:** + +1. Fixed lang parameter bug. +2. Library supports serialization and parcelization. +3. Uploaded the library to Maven Central repository. + + +####2.5.0.3 + +**Implemented:** + +1. Current Weather +2. Daily Forecasts +3. Hourly Forecasts + +**New Features:** + +1. Faster than ever before +2. Raw Response for Caching purposes +3. APIs' URL building using StringBuilder +4. Multi-lingual (multiple languages) support +5. Support for external/third-party HTTP libraries (like Apache's HttpComponents) +6. Units and Language enums for setting configuration easily and correctly +7. Better maintain-able source code (for developers) +8. Ported the project to Gradle (for developers) + +**Changed:** + +1. Package's name from net.aksingh.java.api.owm to net.aksingh.owmjapis +2. Class's name from CurrentWeatherData to CurrentWeather +3. Class's name from DailyForecastData to DailyForecast +4. Class's name from ForecastWeatherData to HourlyForecast +5. Some functions' name and signature + +**Apologies for making such changes, but it was required to make things simpler. Don't worry, they're not going to change again. :)** + + +####2.5.0.2 + +**Bug-fix version:** + +1. Fixed bugs which caused wrong parsing of date and time. +2. Improved code formatting and readability (for developers). + + +####2.5.0.1 + +**Implemented:** + +1. Current Weather +2. Weather Forecasts +3. Daily Forecasts +4. Wind degree to direction converter + +**Not implemented but planned:** + +1. Searching of City +2. Weather Maps +3. Country code to name converter +4. Direction code to name converter diff --git a/build.gradle b/build.gradle index e1fbe15..169c612 100644 --- a/build.gradle +++ b/build.gradle @@ -1,80 +1,133 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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. * + **************************************************************************************************/ + apply plugin: 'java' +apply plugin: 'kotlin' +apply plugin: 'org.jetbrains.dokka' + apply plugin: 'maven' apply plugin: 'signing' -sourceCompatibility = 1.5 +sourceCompatibility = 1.7 group = 'net.aksingh' archivesBaseName = "owm-japis" -version = '2.5.0.5' +version = '2.5.1.0' repositories { - mavenCentral() + mavenCentral() } dependencies { - compile group: 'org.json', name: 'json', version: '20140107' - testCompile group: 'junit', name: 'junit', version: '4.12' + compile 'org.json:json:20171018' + compile 'com.google.code.gson:gson:2.8.2' + + compile 'com.squareup.retrofit2:retrofit:2.3.0' + compile 'com.squareup.retrofit2:converter-gson:2.3.0' + + compile "org.jetbrains.kotlin:kotlin-stdlib" + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" + + testCompile 'junit:junit:4.12' } task javadocJar(type: Jar) { - classifier = 'javadoc' - from javadoc + classifier = 'javadoc' + from javadoc } task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allSource + classifier = 'sources' + from sourceSets.main.allSource } artifacts { - archives javadocJar, sourcesJar + archives javadocJar, sourcesJar } signing { - sign configurations.archives + sign configurations.archives } uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } - snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { - authentication(userName: ossrhUsername, password: ossrhPassword) - } + snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } - pom.project { - name 'OWM JAPIs' - packaging 'jar' - description 'Java Wrapper Library for OpenWeatherMap.org Web APIs' - url 'http://code.aksingh.net/owm-japis' + pom.project { + name 'OWM JAPIs' + packaging 'jar' + description 'Java Wrapper Library for OpenWeatherMap.org Web APIs' + url 'http://go.aksingh.net/owm-japis' - scm { - connection 'scm:svn:http://foo.googlecode.com/svn/trunk/' - developerConnection 'scm:svn:https://foo.googlecode.com/svn/trunk/' - url 'http://foo.googlecode.com/svn/trunk/' - } - - licenses { - license { - name 'The MIT License (MIT)' - url 'http://opensource.org/licenses/MIT' - } - } - - developers { - developer { - id 'akapribot' - name 'Ashutosh Kumar Singh' - email 'me@aksingh.net' - } - } - } + scm { + connection 'scm:svn:http://foo.googlecode.com/svn/trunk/' + developerConnection 'scm:svn:https://foo.googlecode.com/svn/trunk/' + url 'http://foo.googlecode.com/svn/trunk/' } + + licenses { + license { + name 'The MIT License (MIT)' + url 'http://opensource.org/licenses/MIT' + } + } + + developers { + developer { + id 'aksinghnet' + name 'Ashutosh Kumar Singh' + email 'ashutosh@aksingh.net' + } + } + } } + } +} + +dokka { + outputFormat = 'javadoc' + outputDirectory = "$buildDir/javadoc" + + jdkVersion = 7 + impliedPlatforms = ["JVM"] +} + +buildscript { + ext.kotlin_version = '1.1.51' + ext.dokka_version = '0.9.15' + + repositories { + jcenter() + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" + } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b7612167031001b7b84baf2a959e8ea8ad03c011..555652f8c0b41c8b474d88798552112951bef500 100644 GIT binary patch delta 33987 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 H=js0eyY3M; delta 30338 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 gm^-QlK6a8~Dab&BI^ -# -# 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. -# - -#Fri Dec 19 05:07:43 IST 2014 +#################################################################################################### +# Copyright (c) 2013-2017 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. # +#################################################################################################### distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip diff --git a/gradlew b/gradlew index 91a7e26..4453cce 100644 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..f955316 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/settings.gradle b/settings.gradle index 1f3a3b6..30d3a87 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,20 @@ -rootProject.name = 'owm-japis' +/************************************************************************************************** + * Copyright (c) 2013-2017 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. * + **************************************************************************************************/ +rootProject.name = 'owm-japis' diff --git a/src/main/java/net/aksingh/owmjapis/AbstractForecast.java b/src/main/java/net/aksingh/owmjapis/AbstractForecast.java index 0a2482a..6729717 100644 --- a/src/main/java/net/aksingh/owmjapis/AbstractForecast.java +++ b/src/main/java/net/aksingh/owmjapis/AbstractForecast.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -39,211 +36,211 @@ import java.io.Serializable; * @since 2.5.0.3 */ public abstract class AbstractForecast extends AbstractResponse { - /* - JSON Keys - */ - static final String JSON_FORECAST_LIST = "list"; - static final String JSON_MESSAGE = "message"; - static final String JSON_CITY = "city"; - static final String JSON_FORECAST_COUNT = "cnt"; + /* + JSON Keys + */ + static final String JSON_FORECAST_LIST = "list"; + static final String JSON_MESSAGE = "message"; + static final String JSON_CITY = "city"; + static final String JSON_FORECAST_COUNT = "cnt"; - /* - Instance variables - */ - private final double message; + /* + Instance variables + */ + private final double message; - private final City city; - private final int forecastCount; + private final City city; + private final int forecastCount; - /* - Constructors + /* + Constructors + */ + AbstractForecast() { + super(); + + this.message = Double.NaN; + this.forecastCount = 0; + this.city = null; + } + + AbstractForecast(JSONObject jsonObj) { + super(jsonObj); + + this.message = (jsonObj != null) ? jsonObj.optDouble(JSON_MESSAGE, Double.NaN) : Double.NaN; + + this.city = (jsonObj != null) ? new City(jsonObj.optJSONObject(JSON_CITY)) : null; + + this.forecastCount = (jsonObj != null) ? jsonObj.optInt(JSON_FORECAST_COUNT, 0) : 0; + } + + /** + * @return true if message is available, otherwise false. + */ + public boolean hasMessage() { + return (this.message != Double.NaN); + } + + /** + * @return true if count of forecasts is available, otherwise false. + */ + public boolean hasForecastCount() { + return (this.forecastCount != 0); + } + + /** + * @return true if message is available, otherwise false. + */ + public boolean hasCityInstance() { + return (this.city != null); + } + + /** + * @return Message if available, otherwise Double.NaN. + */ + public double getMessage() { + return this.message; + } + + /** + * @return Count of forecasts if available, otherwise 0. + */ + public int getForecastCount() { + return this.forecastCount; + } + + /** + * @return City's instance if available, otherwise null. + */ + public City getCityInstance() { + return this.city; + } + + /** + *

+ * Provides default behaviours for City + *

+ * + * @author Ashutosh Kumar Singh + */ + public static class City implements Serializable { + private static final String JSON_CITY_ID = "id"; + private static final String JSON_CITY_NAME = "name"; + private static final String JSON_CITY_COUNTRY_CODE = "country"; + private static final String JSON_CITY_POPULATION = "population"; + private static final String JSON_CITY_COORD = "coord"; + + private final long cityID; + private final String cityName; + private final String countryCode; + private final long population; + + private final Coord coord; + + City() { + this.cityID = Long.MIN_VALUE; + this.cityName = null; + this.countryCode = null; + this.population = Long.MIN_VALUE; + + this.coord = new Coord(); + } + + City(JSONObject jsonObj) { + this.cityID = (jsonObj != null) ? jsonObj.optLong(JSON_CITY_ID, Long.MIN_VALUE) : Long.MIN_VALUE; + this.cityName = (jsonObj != null) ? jsonObj.optString(JSON_CITY_NAME, null) : null; + this.countryCode = (jsonObj != null) ? jsonObj.optString(JSON_CITY_COUNTRY_CODE, null) : null; + this.population = (jsonObj != null) ? jsonObj.optLong(JSON_CITY_POPULATION, Long.MIN_VALUE) : Long.MIN_VALUE; + + JSONObject jsonObjCoord = (jsonObj != null) ? jsonObj.optJSONObject(JSON_CITY_COORD) : null; + this.coord = (jsonObjCoord != null) ? new Coord(jsonObjCoord) : null; + } + + public boolean hasCityCode() { + return this.cityID != Long.MIN_VALUE; + } + + public boolean hasCityName() { + return this.cityName != null; + } + + public boolean hasCountryCode() { + return this.countryCode != null; + } + + public boolean hasCityPopulation() { + return this.population != Long.MIN_VALUE; + } + + /** + * @return true if Coord instance is available, otherwise false. */ - AbstractForecast() { + public boolean hasCoordInstance() { + return coord != null; + } + + public long getCityCode() { + return this.cityID; + } + + public String getCityName() { + return this.cityName; + } + + public String getCountryCode() { + return this.countryCode; + } + + public long getCityPopulation() { + return this.population; + } + + /** + * @return Coord instance if available, otherwise null. + */ + public Coord getCoordInstance() { + return this.coord; + } + + + public static class Coord extends AbstractWeather.Coord { + + Coord() { super(); + } - this.message = Double.NaN; - this.forecastCount = 0; - this.city = null; - } - - AbstractForecast(JSONObject jsonObj) { + Coord(JSONObject jsonObj) { super(jsonObj); + } + } + } - this.message = (jsonObj != null) ? jsonObj.optDouble(JSON_MESSAGE, Double.NaN) : Double.NaN; - - this.city = (jsonObj != null) ? new City(jsonObj.optJSONObject(JSON_CITY)) : null; - - this.forecastCount = (jsonObj != null) ? jsonObj.optInt(JSON_FORECAST_COUNT, 0) : 0; + /** + *

+ * Parses forecast data (one element in the forecastList) 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 abstract static class Forecast extends AbstractWeather { + Forecast() { + super(); } - /** - * @return true if message is available, otherwise false. - */ - public boolean hasMessage() { - return (this.message != Double.NaN); - } - - /** - * @return true if count of forecasts is available, otherwise false. - */ - public boolean hasForecastCount() { - return (this.forecastCount != 0); - } - - /** - * @return true if message is available, otherwise false. - */ - public boolean hasCityInstance() { - return (this.city != null); - } - - /** - * @return Message if available, otherwise Double.NaN. - */ - public double getMessage() { - return this.message; - } - - /** - * @return Count of forecasts if available, otherwise 0. - */ - public int getForecastCount() { - return this.forecastCount; - } - - /** - * @return City's instance if available, otherwise null. - */ - public City getCityInstance() { - return this.city; - } - - /** - *

- * Provides default behaviours for City - *

- * - * @author Ashutosh Kumar Singh - */ - public static class City implements Serializable { - private static final String JSON_CITY_ID = "id"; - private static final String JSON_CITY_NAME = "name"; - private static final String JSON_CITY_COUNTRY_CODE = "country"; - private static final String JSON_CITY_POPULATION = "population"; - private static final String JSON_CITY_COORD = "coord"; - - private final long cityID; - private final String cityName; - private final String countryCode; - private final long population; - - private final Coord coord; - - City() { - this.cityID = Long.MIN_VALUE; - this.cityName = null; - this.countryCode = null; - this.population = Long.MIN_VALUE; - - this.coord = new Coord(); - } - - City(JSONObject jsonObj) { - this.cityID = (jsonObj != null) ? jsonObj.optLong(JSON_CITY_ID, Long.MIN_VALUE) : Long.MIN_VALUE; - this.cityName = (jsonObj != null) ? jsonObj.optString(JSON_CITY_NAME, null) : null; - this.countryCode = (jsonObj != null) ? jsonObj.optString(JSON_CITY_COUNTRY_CODE, null) : null; - this.population = (jsonObj != null) ? jsonObj.optLong(JSON_CITY_POPULATION, Long.MIN_VALUE) : Long.MIN_VALUE; - - JSONObject jsonObjCoord = (jsonObj != null) ? jsonObj.optJSONObject(JSON_CITY_COORD) : null; - this.coord = (jsonObjCoord != null) ? new Coord(jsonObjCoord) : null; - } - - public boolean hasCityCode() { - return this.cityID != Long.MIN_VALUE; - } - - public boolean hasCityName() { - return this.cityName != null; - } - - public boolean hasCountryCode() { - return this.countryCode != null; - } - - public boolean hasCityPopulation() { - return this.population != Long.MIN_VALUE; - } - - /** - * @return true if Coord instance is available, otherwise false. - */ - public boolean hasCoordInstance() { - return coord != null; - } - - public long getCityCode() { - return this.cityID; - } - - public String getCityName() { - return this.cityName; - } - - public String getCountryCode() { - return this.countryCode; - } - - public long getCityPopulation() { - return this.population; - } - - /** - * @return Coord instance if available, otherwise null. - */ - public Coord getCoordInstance() { - return this.coord; - } - - - public static class Coord extends AbstractWeather.Coord { - - Coord() { - super(); - } - - Coord(JSONObject jsonObj) { - super(jsonObj); - } - } - } - - /** - *

- * Parses forecast data (one element in the forecastList) 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 abstract static class Forecast extends AbstractWeather { - Forecast() { - super(); - } - - Forecast(JSONObject jsonObj) { - super(jsonObj); - } + Forecast(JSONObject jsonObj) { + super(jsonObj); } + } } diff --git a/src/main/java/net/aksingh/owmjapis/AbstractResponse.java b/src/main/java/net/aksingh/owmjapis/AbstractResponse.java index f0bb35d..8132e5b 100644 --- a/src/main/java/net/aksingh/owmjapis/AbstractResponse.java +++ b/src/main/java/net/aksingh/owmjapis/AbstractResponse.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -28,7 +25,7 @@ import java.io.Serializable; /** *

- * Provides default behaviours and implementations for the response from OWM.org + * Provides default behaviours and implementations for the response from OpenWeatherMap.org *

* * @author Ashutosh Kumar Singh @@ -36,62 +33,62 @@ import java.io.Serializable; * @since 2.5.0.3 */ abstract class AbstractResponse implements Serializable { - /* - JSON Keys - */ - private static final String JSON_RESPONSE_CODE = "cod"; + /* + JSON Keys + */ + private static final String JSON_RESPONSE_CODE = "code"; - /* - Instance variables - */ - private final int responseCode; - private final String rawResponse; + /* + Instance variables + */ + private final int responseCode; + private final String rawResponse; - /* - Constructors - */ - AbstractResponse() { - this.rawResponse = null; - this.responseCode = Integer.MIN_VALUE; - } + /* + Constructors + */ + AbstractResponse() { + this.rawResponse = null; + this.responseCode = Integer.MIN_VALUE; + } - AbstractResponse(JSONObject jsonObj) { - this.rawResponse = (jsonObj != null) ? jsonObj.toString() : null; - this.responseCode = (jsonObj != null) ? jsonObj.optInt(JSON_RESPONSE_CODE, Integer.MIN_VALUE) : Integer.MIN_VALUE; - } + AbstractResponse(JSONObject jsonObj) { + this.rawResponse = (jsonObj != null) ? jsonObj.toString() : null; + this.responseCode = (jsonObj != null) ? jsonObj.optInt(JSON_RESPONSE_CODE, Integer.MIN_VALUE) : Integer.MIN_VALUE; + } - /** - * @return true if response is valid (downloaded and parsed correctly), otherwise false. - */ - public boolean isValid() { - return this.responseCode == 200; - } + /** + * @return true if response is valid (downloaded and parsed correctly), otherwise false. + */ + public boolean isValid() { + return this.responseCode == 200; + } - /** - * @return true if response code is available, otherwise false. - */ - public boolean hasResponseCode() { - return this.responseCode != Integer.MIN_VALUE; - } + /** + * @return true if response code is available, otherwise false. + */ + public boolean hasResponseCode() { + return this.responseCode != Integer.MIN_VALUE; + } - /** - * @return true if raw response is available, otherwise false. - */ - public boolean hasRawResponse() { - return this.rawResponse != null; - } + /** + * @return true if raw response is available, otherwise false. + */ + public boolean hasRawResponse() { + return this.rawResponse != null; + } - /** - * @return Response code if available, otherwise Integer.MIN_VALUE. - */ - public int getResponseCode() { - return this.responseCode; - } + /** + * @return Response code if available, otherwise Integer.MIN_VALUE. + */ + public int getResponseCode() { + return this.responseCode; + } - /** - * @return Raw response if available, otherwise null. - */ - public String getRawResponse() { - return this.rawResponse; - } + /** + * @return Raw response if available, otherwise null. + */ + public String getRawResponse() { + return this.rawResponse; + } } diff --git a/src/main/java/net/aksingh/owmjapis/AbstractWeather.java b/src/main/java/net/aksingh/owmjapis/AbstractWeather.java index e9b18fd..7d89a5b 100644 --- a/src/main/java/net/aksingh/owmjapis/AbstractWeather.java +++ b/src/main/java/net/aksingh/owmjapis/AbstractWeather.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -43,478 +40,478 @@ import java.util.List; * @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"; + /* + 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 = "windData"; + private static final String JSON_WEATHER = "weather"; + private static final String JSON_DATE_TIME = "dt"; - /* - Instance variables - */ - private final Date dateTime; + /* + Instance variables + */ + private final Date dateTime; - private final int weatherCount; - private final List weatherList; + private final int weatherCount; + private final List weatherList; - /* - Constructors - */ - AbstractWeather() { - super(); + /* + Constructors + */ + AbstractWeather() { + super(); - this.weatherCount = 0; - this.weatherList = null; - this.dateTime = null; + 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 + this.dateTime = new Date(sec * 1000); + } else { + 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 - 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 (weatherArray != null && 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(); + } - JSONArray weatherArray = (jsonObj != null) ? jsonObj.optJSONArray(JSON_WEATHER) : new JSONArray(); - this.weatherList = (weatherArray != null) ? new ArrayList(weatherArray.length()) : Collections.EMPTY_LIST; - if (weatherArray != null && 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(JSON_CLOUDS_ALL, Double.NaN); } /** - * @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 - *

+ * Tells if percentage of clouds is available or not. * - * @author Ashutosh Kumar Singh - * @version 2013/12/23 - * @since 2.5.0.1 + * @return true if data available, otherwise false */ - 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(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 !Float.isNaN(this.percentOfClouds); - } - - /** - * @return Percentage of all clouds if available, otherwise Float.NaN. - */ - public float getPercentageOfClouds() { - return this.percentOfClouds; - } + public boolean hasPercentageOfClouds() { + return !Float.isNaN(this.percentOfClouds); } /** - *

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

- * - * @author Ashutosh Kumar Singh - * @version 2013/12/23 - * @since 2.5.0.1 + * @return Percentage of all clouds if available, otherwise Float.NaN. */ - abstract public static class Coord implements Serializable { - private static final String JSON_COORD_LATITUDE = "lat"; - private static final String JSON_COORD_LONGITUDE = "lon"; + public float getPercentageOfClouds() { + return this.percentOfClouds; + } + } - private final float lat; - private final float lon; + /** + *

+ * 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"; - Coord() { - this.lat = Float.NaN; - this.lon = Float.NaN; - } + private final float lat; + private final float lon; - Coord(JSONObject jsonObj) { - this.lat = (float) jsonObj.optDouble(JSON_COORD_LATITUDE, Double.NaN); - this.lon = (float) jsonObj.optDouble(JSON_COORD_LONGITUDE, Double.NaN); - } + Coord() { + this.lat = Float.NaN; + this.lon = Float.NaN; + } - /** - * Tells if the latitude of the city is available or not. - * - * @return true if data available, otherwise false - */ - public boolean hasLatitude() { - return !Float.isNaN(this.lat); - } - - /** - * Tells if the longitude of the city is available or not. - * - * @return true if data available, otherwise false - */ - public boolean hasLongitude() { - return !Float.isNaN(this.lon); - } - - /** - * @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; - } + Coord(JSONObject jsonObj) { + this.lat = (float) jsonObj.optDouble(JSON_COORD_LATITUDE, Double.NaN); + this.lon = (float) jsonObj.optDouble(JSON_COORD_LONGITUDE, Double.NaN); } /** - *

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

+ * Tells if the latitude of the city is available or not. * - * @author Ashutosh Kumar Singh - * @version 2013/12/23 - * @since 2.5.0.1 + * @return true if data available, otherwise false */ - 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(JSON_MAIN_TEMP, Double.NaN); - this.minTemp = (float) jsonObj.optDouble(JSON_MAIN_TEMP_MIN, Double.NaN); - this.maxTemp = (float) jsonObj.optDouble(JSON_MAIN_TEMP_MAX, Double.NaN); - this.pressure = (float) jsonObj.optDouble(JSON_MAIN_PRESSURE, Double.NaN); - this.humidity = (float) jsonObj.optDouble(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 !Float.isNaN(this.temp); - } - - /** - * Tells if the minimum temperature of the city is available or not. - * - * @return true if data available, otherwise false - */ - public boolean hasMinTemperature() { - return !Float.isNaN(this.minTemp); - } - - /** - * Tells if the maximum temperature of the city is available or not. - * - * @return true if data available, otherwise false - */ - public boolean hasMaxTemperature() { - return !Float.isNaN(this.maxTemp); - } - - /** - * Tells if pressure of the city is available or not. - * - * @return true if data available, otherwise false - */ - public boolean hasPressure() { - return !Float.isNaN(this.pressure); - } - - /** - * Tells if humidity of the city is available or not. - * - * @return true if data available, otherwise false - */ - public boolean hasHumidity() { - return !Float.isNaN(this.humidity); - } - - /** - * @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; - } + public boolean hasLatitude() { + return !Float.isNaN(this.lat); } /** - *

- * 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 - *

+ * Tells if the longitude of the city is available or not. * - * @author Ashutosh Kumar Singh - * @version 2014/12/27 - * @since 2.5.0.3 + * @return true if data available, otherwise false */ - 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(JSON_WEATHER_ID, Integer.MIN_VALUE); - this.name = jsonObj.optString(JSON_WEATHER_MAIN, null); - this.description = jsonObj.optString(JSON_WEATHER_DESCRIPTION, null); - this.icon = jsonObj.optString(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 && (! "".equals(this.name)); - } - - /** - * Tells if weather's description is available or not. - * - * @return true if data available, otherwise false. - */ - public boolean hasWeatherDescription() { - return this.description != null && (! "".equals(this.description)); - } - - /** - * 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 && (! "".equals(this.icon)); - } - - /** - * @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; - } + public boolean hasLongitude() { + return !Float.isNaN(this.lon); } /** - *

- * Provides default behaviours for Wind. - *

- * - * @author Ashutosh Kumar Singh - * @version 2013/12/23 - * @since 2.5.0.1 + * @return Latitude of the city if available, otherwise Float.NaN. */ - 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(JSON_WIND_SPEED, Double.NaN); - this.degree = (float) jsonObj.optDouble(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 !Float.isNaN(this.speed); - } - - /** - * 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() && (! Float.isNaN(this.degree)); - } - - /** - * @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; - } + 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(JSON_MAIN_TEMP, Double.NaN); + this.minTemp = (float) jsonObj.optDouble(JSON_MAIN_TEMP_MIN, Double.NaN); + this.maxTemp = (float) jsonObj.optDouble(JSON_MAIN_TEMP_MAX, Double.NaN); + this.pressure = (float) jsonObj.optDouble(JSON_MAIN_PRESSURE, Double.NaN); + this.humidity = (float) jsonObj.optDouble(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 !Float.isNaN(this.temp); + } + + /** + * Tells if the minimum temperature of the city is available or not. + * + * @return true if data available, otherwise false + */ + public boolean hasMinTemperature() { + return !Float.isNaN(this.minTemp); + } + + /** + * Tells if the maximum temperature of the city is available or not. + * + * @return true if data available, otherwise false + */ + public boolean hasMaxTemperature() { + return !Float.isNaN(this.maxTemp); + } + + /** + * Tells if pressure of the city is available or not. + * + * @return true if data available, otherwise false + */ + public boolean hasPressure() { + return !Float.isNaN(this.pressure); + } + + /** + * Tells if humidity of the city is available or not. + * + * @return true if data available, otherwise false + */ + public boolean hasHumidity() { + return !Float.isNaN(this.humidity); + } + + /** + * @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(JSON_WEATHER_ID, Integer.MIN_VALUE); + this.name = jsonObj.optString(JSON_WEATHER_MAIN, null); + this.description = jsonObj.optString(JSON_WEATHER_DESCRIPTION, null); + this.icon = jsonObj.optString(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 && (!"".equals(this.name)); + } + + /** + * Tells if weather's description is available or not. + * + * @return true if data available, otherwise false. + */ + public boolean hasWeatherDescription() { + return this.description != null && (!"".equals(this.description)); + } + + /** + * 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 && (!"".equals(this.icon)); + } + + /** + * @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(JSON_WIND_SPEED, Double.NaN); + this.degree = (float) jsonObj.optDouble(JSON_WIND_DEGREE, Double.NaN); + } + + /** + * Tells if speed of windData in the city is available or not. + * + * @return true if data available, otherwise false. + */ + public boolean hasWindSpeed() { + return !Float.isNaN(this.speed); + } + + /** + * Tells if degree (degree gives direction) of windData in the city is available or not. + * + * @return true if data available, otherwise false. + */ + public boolean hasWindDegree() { + return this.hasWindSpeed() && (!Float.isNaN(this.degree)); + } + + /** + * @return Speed of windData in the city if available, otherwise Float.NaN. + */ + public float getWindSpeed() { + return this.speed; + } + + /** + * @return Degree of windData in the city if available, otherwise Float.NaN. + */ + public float getWindDegree() { + return this.degree; + } + } } diff --git a/src/main/java/net/aksingh/owmjapis/CurrentWeather.java b/src/main/java/net/aksingh/owmjapis/CurrentWeather.java index 7aa16e6..f54366f 100644 --- a/src/main/java/net/aksingh/owmjapis/CurrentWeather.java +++ b/src/main/java/net/aksingh/owmjapis/CurrentWeather.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -45,569 +42,569 @@ import java.util.Date; * * @author Ashutosh Kumar Singh * @version 2014/12/26 - * @see
OWM's Current Weather API + * @see OWM's Current Weather API * @since 2.5.0.1 */ public class CurrentWeather extends AbstractWeather { - /* - JSON Keys - */ - private static final String JSON_RAIN = "rain"; - private static final String JSON_SNOW = "snow"; - private static final String JSON_SYS = "sys"; - private static final String JSON_BASE = "base"; - private static final String JSON_CITY_ID = "id"; - private static final String JSON_CITY_NAME = "name"; + /* + JSON Keys + */ + private static final String JSON_RAIN = "rain"; + private static final String JSON_SNOW = "snow"; + private static final String JSON_SYS = "sys"; + private static final String JSON_BASE = "base"; + private static final String JSON_CITY_ID = "id"; + private static final String JSON_CITY_NAME = "name"; - /* - Instance variables - */ - private final String base; - private final long cityId; - private final String cityName; + /* + Instance variables + */ + private final String base; + private final long cityId; + private final String cityName; - private final Clouds clouds; - private final Coord coord; - private final Main main; - private final Rain rain; - private final Snow snow; - private final Sys sys; - private final Wind wind; + private final Clouds clouds; + private final Coord coord; + private final Main main; + private final Rain rain; + private final Snow snow; + private final Sys sys; + private final Wind wind; - /* - Constructor - */ - CurrentWeather(JSONObject jsonObj) { - super(jsonObj); + /* + Constructor + */ + CurrentWeather(JSONObject jsonObj) { + super(jsonObj); - this.base = (jsonObj != null) ? jsonObj.optString(JSON_BASE, null) : null; - this.cityId = (jsonObj != null) ? jsonObj.optLong(JSON_CITY_ID, Long.MIN_VALUE) : Long.MIN_VALUE; - this.cityName = (jsonObj != null) ? jsonObj.optString(JSON_CITY_NAME, null) : null; + this.base = (jsonObj != null) ? jsonObj.optString(JSON_BASE, null) : null; + this.cityId = (jsonObj != null) ? jsonObj.optLong(JSON_CITY_ID, Long.MIN_VALUE) : Long.MIN_VALUE; + this.cityName = (jsonObj != null) ? jsonObj.optString(JSON_CITY_NAME, null) : null; - JSONObject cloudsObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_CLOUDS) : null; - this.clouds = (cloudsObj != null) ? new Clouds(cloudsObj) : null; + JSONObject cloudsObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_CLOUDS) : null; + this.clouds = (cloudsObj != null) ? new Clouds(cloudsObj) : null; - JSONObject coordObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_COORD) : null; - this.coord = (coordObj != null) ? new Coord(coordObj) : null; + JSONObject coordObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_COORD) : null; + this.coord = (coordObj != null) ? new Coord(coordObj) : null; - JSONObject mainObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_MAIN) : null; - this.main = (mainObj != null) ? new Main(mainObj) : null; + JSONObject mainObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_MAIN) : null; + this.main = (mainObj != null) ? new Main(mainObj) : null; - JSONObject rainObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_RAIN) : null; - this.rain = (rainObj != null) ? new Rain(rainObj) : null; + JSONObject rainObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_RAIN) : null; + this.rain = (rainObj != null) ? new Rain(rainObj) : null; - JSONObject snowObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_SNOW) : null; - this.snow = (snowObj != null) ? new Snow(snowObj) : null; + JSONObject snowObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_SNOW) : null; + this.snow = (snowObj != null) ? new Snow(snowObj) : null; - JSONObject sysObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_SYS) : null; - this.sys = (sysObj != null) ? new Sys(sysObj) : null; + JSONObject sysObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_SYS) : null; + this.sys = (sysObj != null) ? new Sys(sysObj) : null; - JSONObject windObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_WIND) : null; - this.wind = (windObj != null) ? new Wind(windObj) : null; + JSONObject windObj = (jsonObj != null) ? jsonObj.optJSONObject(JSON_WIND) : null; + this.wind = (windObj != null) ? new Wind(windObj) : null; + } + + /** + * @return true if base station is available, otherwise false. + */ + public boolean hasBaseStation() { + return this.base != null && (!"".equals(this.base)); + } + + /** + * @return true if city code is available, otherwise false. + */ + public boolean hasCityCode() { + return this.cityId != Long.MIN_VALUE; + } + + /** + * @return true if city name is available, otherwise false. + */ + public boolean hasCityName() { + return this.cityName != null && (!"".equals(this.cityName)); + } + + /** + * @return true if Cloud instance is available, otherwise false. + */ + public boolean hasCloudsInstance() { + return clouds != null; + } + + /** + * @return true if Coord instance is available, otherwise false. + */ + public boolean hasCoordInstance() { + return coord != null; + } + + /** + * @return true if Main instance is available, otherwise false. + */ + public boolean hasMainInstance() { + return main != null; + } + + /** + * @return true if Rain instance is available, otherwise false. + */ + public boolean hasRainInstance() { + return rain != null; + } + + /** + * @return true if Snow instance is available, otherwise false. + */ + public boolean hasSnowInstance() { + return snow != null; + } + + /** + * @return true if System instance is available, otherwise false. + */ + public boolean hasSysInstance() { + return sys != null; + } + + /** + * @return true if Wind instance is available, otherwise false. + */ + public boolean hasWindInstance() { + return wind != null; + } + + /** + * @return Base station if available, otherwise null. + */ + public String getBaseStation() { + return this.base; + } + + /** + * @return City code if available, otherwise Long.MIN_VALUE. + */ + public long getCityCode() { + return this.cityId; + } + + /** + * @return City name if available, otherwise null. + */ + public String getCityName() { + return this.cityName; + } + + /** + * @return Cloud instance if available, otherwise null. + */ + public Clouds getCloudsInstance() { + return this.clouds; + } + + /** + * @return Coord instance if available, otherwise null. + */ + public Coord getCoordInstance() { + return this.coord; + } + + /** + * @return Main instance if available, otherwise null. + */ + public Main getMainInstance() { + return this.main; + } + + /** + * @return Rain instance if available, otherwise null. + */ + public Rain getRainInstance() { + return this.rain; + } + + /** + * @return Snow instance if available, otherwise null. + */ + public Snow getSnowInstance() { + return this.snow; + } + + /** + * @return System instance if available, otherwise null. + */ + public Sys getSysInstance() { + return this.sys; + } + + /** + * @return Wind instance if available, otherwise null. + */ + public Wind getWindInstance() { + return this.wind; + } + + /** + *

+ * Parses clouds 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/26 + * @since 2.5.0.1 + */ + public static class Clouds extends AbstractWeather.Clouds { + + Clouds() { + super(); } - /** - * @return true if base station is available, otherwise false. - */ - public boolean hasBaseStation() { - return this.base != null && (! "".equals(this.base)); + Clouds(JSONObject jsonObj) { + super(jsonObj); + } + } + + /** + *

+ * Parses coordination 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/26 + * @since 2.5.0.1 + */ + public static class Coord extends AbstractWeather.Coord { + + Coord() { + super(); } - /** - * @return true if city code is available, otherwise false. - */ - public boolean hasCityCode() { - return this.cityId != Long.MIN_VALUE; + Coord(JSONObject jsonObj) { + super(jsonObj); + } + } + + /** + *

+ * Parses main 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/26 + * @since 2.5.0.1 + */ + public static class Main extends AbstractWeather.Main { + + Main() { + super(); } - /** - * @return true if city name is available, otherwise false. - */ - public boolean hasCityName() { - return this.cityName != null && (! "".equals(this.cityName)); + Main(JSONObject jsonObj) { + super(jsonObj); + } + } + + /** + *

+ * Parses rain 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/26 + * @since 2.5.0.1 + */ + public static class Rain implements Serializable { + + private static final String JSON_RAIN_1HOUR = "1h"; + private static final String JSON_RAIN_3HOUR = "3h"; + + private final float rain1h; + private final float rain3h; + + Rain() { + this.rain1h = Float.NaN; + this.rain3h = Float.NaN; } - /** - * @return true if Clouds instance is available, otherwise false. - */ - public boolean hasCloudsInstance() { - return clouds != null; + Rain(JSONObject jsonObj) { + this.rain1h = (float) jsonObj.optDouble(JSON_RAIN_1HOUR, Double.NaN); + this.rain3h = (float) jsonObj.optDouble(JSON_RAIN_3HOUR, Double.NaN); } - /** - * @return true if Coord instance is available, otherwise false. - */ - public boolean hasCoordInstance() { - return coord != null; + public boolean hasRain1h() { + return !Float.isNaN(this.rain1h); } - /** - * @return true if Main instance is available, otherwise false. - */ - public boolean hasMainInstance() { - return main != null; + public boolean hasRain3h() { + return !Float.isNaN(this.rain3h); } - /** - * @return true if Rain instance is available, otherwise false. - */ - public boolean hasRainInstance() { - return rain != null; + public float getRain1h() { + return this.rain1h; } - /** - * @return true if Snow instance is available, otherwise false. - */ - public boolean hasSnowInstance() { - return snow != null; + public float getRain3h() { + return this.rain3h; + } + } + + /** + *

+ * Parses snow 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 2015/01/28 + * @since 2.5.0.4 + */ + public static class Snow implements Serializable { + + private static final String JSON_SNOW_1HOUR = "1h"; + private static final String JSON_SNOW_3HOUR = "3h"; + + private final float snow1h; + private final float snow3h; + + Snow() { + this.snow1h = Float.NaN; + this.snow3h = Float.NaN; } - /** - * @return true if Sys instance is available, otherwise false. - */ - public boolean hasSysInstance() { - return sys != null; + Snow(JSONObject jsonObj) { + this.snow1h = (float) jsonObj.optDouble(JSON_SNOW_1HOUR, Double.NaN); + this.snow3h = (float) jsonObj.optDouble(JSON_SNOW_3HOUR, Double.NaN); } - /** - * @return true if Wind instance is available, otherwise false. - */ - public boolean hasWindInstance() { - return wind != null; + public boolean hasSnow1h() { + return !Float.isNaN(this.snow1h); } - /** - * @return Base station if available, otherwise null. - */ - public String getBaseStation() { - return this.base; + public boolean hasSnow3h() { + return !Float.isNaN(this.snow3h); } - /** - * @return City code if available, otherwise Long.MIN_VALUE. - */ - public long getCityCode() { - return this.cityId; + public float getSnow1h() { + return this.snow1h; } - /** - * @return City name if available, otherwise null. - */ - public String getCityName() { - return this.cityName; + public float getSnow3h() { + return this.snow3h; + } + } + + /** + *

+ * Parses sys 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/26 + * @since 2.5.0.1 + */ + public static class Sys implements Serializable { + + private static final String JSON_SYS_TYPE = "type"; + private static final String JSON_SYS_ID = "id"; + private static final String JSON_SYS_MESSAGE = "message"; + private static final String JSON_SYS_COUNTRY_CODE = "country"; + private static final String JSON_SYS_SUNRISE = "sunrise"; + private static final String JSON_SYS_SUNSET = "sunset"; + + private final int type; + private final int id; + private final double message; + private final String countryCode; + private final Date sunrise; + private final Date sunset; + + Sys() { + this.type = Integer.MIN_VALUE; + this.id = Integer.MIN_VALUE; + this.message = Double.NaN; + this.countryCode = null; + this.sunrise = null; + this.sunset = null; } - /** - * @return Clouds instance if available, otherwise null. - */ - public Clouds getCloudsInstance() { - return this.clouds; + Sys(JSONObject jsonObj) { + this.type = jsonObj.optInt(JSON_SYS_TYPE, Integer.MIN_VALUE); + this.id = jsonObj.optInt(JSON_SYS_ID, Integer.MIN_VALUE); + this.message = jsonObj.optDouble(JSON_SYS_MESSAGE, Double.NaN); + this.countryCode = jsonObj.optString(JSON_SYS_COUNTRY_CODE, null); + + long sr_secs = jsonObj.optLong(JSON_SYS_SUNRISE, Long.MIN_VALUE); + if (sr_secs != Long.MIN_VALUE) { + this.sunrise = new Date(sr_secs * 1000); + } else { + this.sunrise = null; + } + + long ss_secs = jsonObj.optLong(JSON_SYS_SUNSET, Long.MIN_VALUE); + if (ss_secs != Long.MIN_VALUE) { + this.sunset = new Date(ss_secs * 1000); + } else { + this.sunset = null; + } } - /** - * @return Coord instance if available, otherwise null. - */ - public Coord getCoordInstance() { - return this.coord; + public boolean hasType() { + return this.type != Integer.MIN_VALUE; } - /** - * @return Main instance if available, otherwise null. - */ - public Main getMainInstance() { - return this.main; + public boolean hasId() { + return this.id != Integer.MIN_VALUE; } - /** - * @return Rain instance if available, otherwise null. - */ - public Rain getRainInstance() { - return this.rain; + public boolean hasMessage() { + return !Double.isNaN(this.message); } - /** - * @return Snow instance if available, otherwise null. - */ - public Snow getSnowInstance() { - return this.snow; + public boolean hasCountryCode() { + return this.countryCode != null && (!"".equals(this.countryCode)); } - /** - * @return Sys instance if available, otherwise null. - */ - public Sys getSysInstance() { - return this.sys; + public boolean hasSunriseTime() { + return this.sunrise != null; } - /** - * @return Wind instance if available, otherwise null. - */ - public Wind getWindInstance() { - return this.wind; + public boolean hasSunsetTime() { + return this.sunset != null; } - /** - *

- * Parses clouds 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/26 - * @since 2.5.0.1 - */ - public static class Clouds extends AbstractWeather.Clouds { - - Clouds() { - super(); - } - - Clouds(JSONObject jsonObj) { - super(jsonObj); - } + public int getType() { + return this.type; } - /** - *

- * Parses coordination 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/26 - * @since 2.5.0.1 - */ - public static class Coord extends AbstractWeather.Coord { - - Coord() { - super(); - } - - Coord(JSONObject jsonObj) { - super(jsonObj); - } + public int getId() { + return this.id; } - /** - *

- * Parses main 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/26 - * @since 2.5.0.1 - */ - public static class Main extends AbstractWeather.Main { - - Main() { - super(); - } - - Main(JSONObject jsonObj) { - super(jsonObj); - } + public double getMessage() { + return this.message; } - /** - *

- * Parses rain 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/26 - * @since 2.5.0.1 - */ - public static class Rain implements Serializable { - - private static final String JSON_RAIN_1HOUR = "1h"; - private static final String JSON_RAIN_3HOUR = "3h"; - - private final float rain1h; - private final float rain3h; - - Rain() { - this.rain1h = Float.NaN; - this.rain3h = Float.NaN; - } - - Rain(JSONObject jsonObj) { - this.rain1h = (float) jsonObj.optDouble(JSON_RAIN_1HOUR, Double.NaN); - this.rain3h = (float) jsonObj.optDouble(JSON_RAIN_3HOUR, Double.NaN); - } - - public boolean hasRain1h() { - return !Float.isNaN(this.rain1h); - } - - public boolean hasRain3h() { - return !Float.isNaN(this.rain3h); - } - - public float getRain1h() { - return this.rain1h; - } - - public float getRain3h() { - return this.rain3h; - } + public String getCountryCode() { + return this.countryCode; } - /** - *

- * Parses snow 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 2015/01/28 - * @since 2.5.0.4 - */ - public static class Snow implements Serializable { - - private static final String JSON_SNOW_1HOUR = "1h"; - private static final String JSON_SNOW_3HOUR = "3h"; - - private final float snow1h; - private final float snow3h; - - Snow() { - this.snow1h = Float.NaN; - this.snow3h = Float.NaN; - } - - Snow(JSONObject jsonObj) { - this.snow1h = (float) jsonObj.optDouble(JSON_SNOW_1HOUR, Double.NaN); - this.snow3h = (float) jsonObj.optDouble(JSON_SNOW_3HOUR, Double.NaN); - } - - public boolean hasSnow1h() { - return !Float.isNaN(this.snow1h); - } - - public boolean hasSnow3h() { - return !Float.isNaN(this.snow3h); - } - - public float getSnow1h() { - return this.snow1h; - } - - public float getSnow3h() { - return this.snow3h; - } + public Date getSunriseTime() { + return this.sunrise; } - /** - *

- * Parses sys 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/26 - * @since 2.5.0.1 - */ - public static class Sys implements Serializable { + public Date getSunsetTime() { + return this.sunset; + } + } - private static final String JSON_SYS_TYPE = "type"; - private static final String JSON_SYS_ID = "id"; - private static final String JSON_SYS_MESSAGE = "message"; - private static final String JSON_SYS_COUNTRY_CODE = "country"; - private static final String JSON_SYS_SUNRISE = "sunrise"; - private static final String JSON_SYS_SUNSET = "sunset"; + /** + *

+ * Parses windData 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/26 + * @since 2.5.0.1 + */ + public static class Wind extends AbstractWeather.Wind { - private final int type; - private final int id; - private final double message; - private final String countryCode; - private final Date sunrise; - private final Date sunset; + private static final String JSON_WIND_GUST = "gust"; - Sys() { - this.type = Integer.MIN_VALUE; - this.id = Integer.MIN_VALUE; - this.message = Double.NaN; - this.countryCode = null; - this.sunrise = null; - this.sunset = null; - } + private final float gust; - Sys(JSONObject jsonObj) { - this.type = jsonObj.optInt(JSON_SYS_TYPE, Integer.MIN_VALUE); - this.id = jsonObj.optInt(JSON_SYS_ID, Integer.MIN_VALUE); - this.message = jsonObj.optDouble(JSON_SYS_MESSAGE, Double.NaN); - this.countryCode = jsonObj.optString(JSON_SYS_COUNTRY_CODE, null); + Wind() { + super(); - long sr_secs = jsonObj.optLong(JSON_SYS_SUNRISE, Long.MIN_VALUE); - if (sr_secs != Long.MIN_VALUE) { - this.sunrise = new Date(sr_secs * 1000); - } else { - this.sunrise = null; - } - - long ss_secs = jsonObj.optLong(JSON_SYS_SUNSET, Long.MIN_VALUE); - if (ss_secs != Long.MIN_VALUE) { - this.sunset = new Date(ss_secs * 1000); - } else { - this.sunset = null; - } - } - - public boolean hasType() { - return this.type != Integer.MIN_VALUE; - } - - public boolean hasId() { - return this.id != Integer.MIN_VALUE; - } - - public boolean hasMessage() { - return !Double.isNaN(this.message); - } - - public boolean hasCountryCode() { - return this.countryCode != null && (! "".equals(this.countryCode)); - } - - public boolean hasSunriseTime() { - return this.sunrise != null; - } - - public boolean hasSunsetTime() { - return this.sunset != null; - } - - public int getType() { - return this.type; - } - - public int getId() { - return this.id; - } - - public double getMessage() { - return this.message; - } - - public String getCountryCode() { - return this.countryCode; - } - - public Date getSunriseTime() { - return this.sunrise; - } - - public Date getSunsetTime() { - return this.sunset; - } + this.gust = Float.NaN; } - /** - *

- * Parses wind 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/26 - * @since 2.5.0.1 - */ - public static class Wind extends AbstractWeather.Wind { + Wind(JSONObject jsonObj) { + super(jsonObj); - private static final String JSON_WIND_GUST = "gust"; - - private final float gust; - - Wind() { - super(); - - this.gust = Float.NaN; - } - - Wind(JSONObject jsonObj) { - super(jsonObj); - - this.gust = (float) jsonObj.optDouble(JSON_WIND_GUST, Double.NaN); - } - - public boolean hasWindGust() { - return !Float.isNaN(this.gust); - } - - public float getWindGust() { - return this.gust; - } + this.gust = (float) jsonObj.optDouble(JSON_WIND_GUST, Double.NaN); } + + public boolean hasWindGust() { + return !Float.isNaN(this.gust); + } + + public float getWindGust() { + return this.gust; + } + } } diff --git a/src/main/java/net/aksingh/owmjapis/DailyForecast.java b/src/main/java/net/aksingh/owmjapis/DailyForecast.java index c66f940..5bb928e 100644 --- a/src/main/java/net/aksingh/owmjapis/DailyForecast.java +++ b/src/main/java/net/aksingh/owmjapis/DailyForecast.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -48,44 +45,179 @@ import java.util.List; * * @author Ashutosh Kumar Singh * @version 2014/12/27 - * @see OWM's Weather Forecast API + * @see OWM's Weather Data API * @since 2.5.0.3 */ public class DailyForecast extends AbstractForecast { + /* + Instance variables + */ + private final List forecastList; + + /* + Constructors + */ + DailyForecast(JSONObject jsonObj) { + super(jsonObj); + + JSONArray dataArray = (jsonObj != null) ? jsonObj.optJSONArray(JSON_FORECAST_LIST) : new JSONArray(); + this.forecastList = (dataArray != null) ? new ArrayList(dataArray.length()) : Collections.EMPTY_LIST; + if (dataArray != null && this.forecastList != Collections.EMPTY_LIST) { + for (int i = 0; i < dataArray.length(); i++) { + JSONObject forecastObj = dataArray.optJSONObject(i); + if (forecastObj != null) { + this.forecastList.add(new Forecast(forecastObj)); + } + } + } + } + + /** + * @param index Index of Data instance in the list. + * @return Data instance if available, otherwise null. + */ + public Forecast getForecastInstance(int index) { + return this.forecastList.get(index); + } + + /** + *

+ * Parses forecast data (one element in the forecastList) 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 + *

+ */ + public static class Forecast extends AbstractForecast.Forecast { /* - Instance variables + JSON Keys */ - private final List forecastList; + public static final String JSON_TEMP = "temp"; + + private static final String JSON_FORECAST_PRESSURE = "pressure"; + private static final String JSON_FORECAST_HUMIDITY = "humidity"; + private static final String JSON_FORECAST_WIND_SPEED = "speed"; + private static final String JSON_FORECAST_WIND_DEGREE = "deg"; + private static final String JSON_FORECAST_CLOUDS = "clouds"; + private static final String JSON_FORECAST_RAIN = "rain"; + private static final String JSON_FORECAST_SNOW = "snow"; + + /* + Instance Variables + */ + private final float pressure; + private final float humidity; + private final float windSpeed; + private final float windDegree; + private final float cloudsPercent; + private final float rain; + private final float snow; + + private final Temperature temp; /* Constructors */ - DailyForecast(JSONObject jsonObj) { - super(jsonObj); + Forecast() { + super(); - JSONArray dataArray = (jsonObj != null) ? jsonObj.optJSONArray(JSON_FORECAST_LIST) : new JSONArray(); - this.forecastList = (dataArray != null) ? new ArrayList(dataArray.length()) : Collections.EMPTY_LIST; - if (dataArray != null && this.forecastList != Collections.EMPTY_LIST) { - for (int i = 0; i < dataArray.length(); i++) { - JSONObject forecastObj = dataArray.optJSONObject(i); - if (forecastObj != null) { - this.forecastList.add(new Forecast(forecastObj)); - } - } - } + this.pressure = Float.NaN; + this.humidity = Float.NaN; + this.windSpeed = Float.NaN; + this.windDegree = Float.NaN; + this.cloudsPercent = Float.NaN; + this.rain = Float.NaN; + this.snow = Float.NaN; + + this.temp = new Temperature(); } - /** - * @param index Index of Forecast instance in the list. - * @return Forecast instance if available, otherwise null. - */ - public Forecast getForecastInstance(int index) { - return this.forecastList.get(index); + Forecast(JSONObject jsonObj) { + super(jsonObj); + + JSONObject jsonObjTemp = (jsonObj != null) ? jsonObj.optJSONObject(JSON_TEMP) : null; + this.temp = (jsonObjTemp != null) ? new Temperature(jsonObjTemp) : new Temperature(); + + this.humidity = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_HUMIDITY, Double.NaN) : Float.NaN; + this.pressure = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_PRESSURE, Double.NaN) : Float.NaN; + this.windSpeed = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_WIND_SPEED, Double.NaN) : Float.NaN; + this.windDegree = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_WIND_DEGREE, Double.NaN) : Float.NaN; + this.cloudsPercent = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_CLOUDS, Double.NaN) : Float.NaN; + this.rain = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_RAIN, Double.NaN) : Float.NaN; + this.snow = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_SNOW, Double.NaN) : Float.NaN; + } + + public boolean hasHumidity() { + return !Float.isNaN(this.humidity); + } + + public boolean hasPressure() { + return !Float.isNaN(this.pressure); + } + + public boolean hasWindSpeed() { + return !Float.isNaN(this.windSpeed); + } + + public boolean hasWindDegree() { + return !Float.isNaN(this.windDegree); + } + + public boolean hasPercentageOfClouds() { + return !Float.isNaN(this.cloudsPercent); + } + + public boolean hasRain() { + return !Float.isNaN(this.rain); + } + + public boolean hasSnow() { + return !Float.isNaN(this.snow); + } + + public float getHumidity() { + return this.humidity; + } + + public float getPressure() { + return this.pressure; + } + + public float getWindSpeed() { + return this.windSpeed; + } + + public float getWindDegree() { + return this.windDegree; + } + + public float getPercentageOfClouds() { + return this.cloudsPercent; + } + + public float getRain() { + return this.rain; + } + + public float getSnow() { + return this.snow; + } + + public Temperature getTemperatureInstance() { + return this.temp; } /** *

- * Parses forecast data (one element in the forecastList) and provides methods to get/access the same information. + * Parses temperature data and provides methods to get/access the same information. * This class provides has and get methods to access the information. *

*

@@ -99,221 +231,86 @@ public class DailyForecast extends AbstractForecast { * Others: null *

*/ - public static class Forecast extends AbstractForecast.Forecast { - /* - JSON Keys - */ - public static final String JSON_TEMP = "temp"; + public static class Temperature implements Serializable { + private static final String JSON_TEMP_DAY = "day"; + private static final String JSON_TEMP_MIN = "min"; + private static final String JSON_TEMP_MAX = "max"; + private static final String JSON_TEMP_NIGHT = "night"; + private static final String JSON_TEMP_EVENING = "eve"; + private static final String JSON_TEMP_MORNING = "morn"; - private static final String JSON_FORECAST_PRESSURE = "pressure"; - private static final String JSON_FORECAST_HUMIDITY = "humidity"; - private static final String JSON_FORECAST_WIND_SPEED = "speed"; - private static final String JSON_FORECAST_WIND_DEGREE = "deg"; - private static final String JSON_FORECAST_CLOUDS = "clouds"; - private static final String JSON_FORECAST_RAIN = "rain"; - private static final String JSON_FORECAST_SNOW = "snow"; + private final float dayTemp; + private final float minTemp; + private final float maxTemp; + private final float nightTemp; + private final float eveTemp; + private final float mornTemp; - /* - Instance Variables - */ - private final float pressure; - private final float humidity; - private final float windSpeed; - private final float windDegree; - private final float cloudsPercent; - private final float rain; - private final float snow; + Temperature() { + this.dayTemp = Float.NaN; + this.minTemp = Float.NaN; + this.maxTemp = Float.NaN; + this.nightTemp = Float.NaN; + this.eveTemp = Float.NaN; + this.mornTemp = Float.NaN; + } - private final Temperature temp; + Temperature(JSONObject jsonObj) { + this.dayTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_DAY, Double.NaN) : Float.NaN; + this.minTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_MIN, Double.NaN) : Float.NaN; + this.maxTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_MAX, Double.NaN) : Float.NaN; + this.nightTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_NIGHT, Double.NaN) : Float.NaN; + this.eveTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_EVENING, Double.NaN) : Float.NaN; + this.mornTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_MORNING, Double.NaN) : Float.NaN; + } - /* - Constructors - */ - Forecast() { - super(); + public boolean hasDayTemperature() { + return !Float.isNaN(this.dayTemp); + } - this.pressure = Float.NaN; - this.humidity = Float.NaN; - this.windSpeed = Float.NaN; - this.windDegree = Float.NaN; - this.cloudsPercent = Float.NaN; - this.rain = Float.NaN; - this.snow = Float.NaN; + public boolean hasMinimumTemperature() { + return !Float.isNaN(this.minTemp); + } - this.temp = new Temperature(); - } + public boolean hasMaximumTemperature() { + return !Float.isNaN(this.maxTemp); + } - Forecast(JSONObject jsonObj) { - super(jsonObj); + public boolean hasNightTemperature() { + return !Float.isNaN(this.nightTemp); + } - JSONObject jsonObjTemp = (jsonObj != null) ? jsonObj.optJSONObject(JSON_TEMP) : null; - this.temp = (jsonObjTemp != null) ? new Temperature(jsonObjTemp) : new Temperature(); + public boolean hasEveningTemperature() { + return !Float.isNaN(this.eveTemp); + } - this.humidity = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_HUMIDITY, Double.NaN) : Float.NaN; - this.pressure = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_PRESSURE, Double.NaN) : Float.NaN; - this.windSpeed = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_WIND_SPEED, Double.NaN) : Float.NaN; - this.windDegree = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_WIND_DEGREE, Double.NaN) : Float.NaN; - this.cloudsPercent = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_CLOUDS, Double.NaN) : Float.NaN; - this.rain = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_RAIN, Double.NaN) : Float.NaN; - this.snow = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_FORECAST_SNOW, Double.NaN) : Float.NaN; - } + public boolean hasMorningTemperature() { + return !Float.isNaN(this.mornTemp); + } - public boolean hasHumidity() { - return !Float.isNaN(this.humidity); - } + public float getDayTemperature() { + return this.dayTemp; + } - public boolean hasPressure() { - return !Float.isNaN(this.pressure); - } + public float getMinimumTemperature() { + return this.minTemp; + } - public boolean hasWindSpeed() { - return !Float.isNaN(this.windSpeed); - } + public float getMaximumTemperature() { + return this.maxTemp; + } - public boolean hasWindDegree() { - return !Float.isNaN(this.windDegree); - } + public float getNightTemperature() { + return this.nightTemp; + } - public boolean hasPercentageOfClouds() { - return !Float.isNaN(this.cloudsPercent); - } + public float getEveningTemperature() { + return this.eveTemp; + } - public boolean hasRain() { - return !Float.isNaN(this.rain); - } - - public boolean hasSnow() { - return !Float.isNaN(this.snow); - } - - public float getHumidity() { - return this.humidity; - } - - public float getPressure() { - return this.pressure; - } - - public float getWindSpeed() { - return this.windSpeed; - } - - public float getWindDegree() { - return this.windDegree; - } - - public float getPercentageOfClouds() { - return this.cloudsPercent; - } - - public float getRain() { - return this.rain; - } - - public float getSnow() { - return this.snow; - } - - public Temperature getTemperatureInstance() { - return this.temp; - } - - /** - *

- * Parses temperature 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 - *

- */ - public static class Temperature implements Serializable { - private static final String JSON_TEMP_DAY = "day"; - private static final String JSON_TEMP_MIN = "min"; - private static final String JSON_TEMP_MAX = "max"; - private static final String JSON_TEMP_NIGHT = "night"; - private static final String JSON_TEMP_EVENING = "eve"; - private static final String JSON_TEMP_MORNING = "morn"; - - private final float dayTemp; - private final float minTemp; - private final float maxTemp; - private final float nightTemp; - private final float eveTemp; - private final float mornTemp; - - Temperature() { - this.dayTemp = Float.NaN; - this.minTemp = Float.NaN; - this.maxTemp = Float.NaN; - this.nightTemp = Float.NaN; - this.eveTemp = Float.NaN; - this.mornTemp = Float.NaN; - } - - Temperature(JSONObject jsonObj) { - this.dayTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_DAY, Double.NaN) : Float.NaN; - this.minTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_MIN, Double.NaN) : Float.NaN; - this.maxTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_MAX, Double.NaN) : Float.NaN; - this.nightTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_NIGHT, Double.NaN) : Float.NaN; - this.eveTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_EVENING, Double.NaN) : Float.NaN; - this.mornTemp = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_TEMP_MORNING, Double.NaN) : Float.NaN; - } - - public boolean hasDayTemperature() { - return !Float.isNaN(this.dayTemp); - } - - public boolean hasMinimumTemperature() { - return !Float.isNaN(this.minTemp); - } - - public boolean hasMaximumTemperature() { - return !Float.isNaN(this.maxTemp); - } - - public boolean hasNightTemperature() { - return !Float.isNaN(this.nightTemp); - } - - public boolean hasEveningTemperature() { - return !Float.isNaN(this.eveTemp); - } - - public boolean hasMorningTemperature() { - return !Float.isNaN(this.mornTemp); - } - - public float getDayTemperature() { - return this.dayTemp; - } - - public float getMinimumTemperature() { - return this.minTemp; - } - - public float getMaximumTemperature() { - return this.maxTemp; - } - - public float getNightTemperature() { - return this.nightTemp; - } - - public float getEveningTemperature() { - return this.eveTemp; - } - - public float getMorningTemperature() { - return this.mornTemp; - } - } + public float getMorningTemperature() { + return this.mornTemp; + } } + } } diff --git a/src/main/java/net/aksingh/owmjapis/HourlyForecast.java b/src/main/java/net/aksingh/owmjapis/HourlyForecast.java index ef9174f..ec37721 100644 --- a/src/main/java/net/aksingh/owmjapis/HourlyForecast.java +++ b/src/main/java/net/aksingh/owmjapis/HourlyForecast.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -48,44 +45,162 @@ import java.util.List; * * @author Ashutosh Kumar Singh * @version 2014/12/27 - * @see OWM's Weather Forecast API + * @see OWM's Weather Data API * @since 2.5.0.3 */ public class HourlyForecast extends AbstractForecast { + /* + Instance variables + */ + private final List forecastList; + + /* + Constructor + */ + HourlyForecast(JSONObject jsonObj) { + super(jsonObj); + + JSONArray forecastArr = (jsonObj != null) ? jsonObj.optJSONArray(this.JSON_FORECAST_LIST) : new JSONArray(); + this.forecastList = (forecastArr != null) ? new ArrayList(forecastArr.length()) : Collections.EMPTY_LIST; + if (forecastArr != null && this.forecastList != Collections.EMPTY_LIST) { + for (int i = 0; i < forecastArr.length(); i++) { + JSONObject forecastObj = forecastArr.optJSONObject(i); + if (forecastObj != null) { + this.forecastList.add(new Forecast(forecastObj)); + } + } + } + } + + /** + * @param index Index of Data instance in the list. + * @return Data instance if available, otherwise null. + */ + public Forecast getForecastInstance(int index) { + return this.forecastList.get(index); + } + + /** + *

+ * Parses forecast data (one element in the forecastList) 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 + *

+ */ + public static class Forecast extends AbstractForecast.Forecast { /* - Instance variables + JSON Keys */ - private final List forecastList; + private static final String JSON_SYS = "sys"; + private static final String JSON_DT_TEXT = "dt_txt"; + + /* + Instance Variables + */ + private final String dateTimeText; + + private final Clouds clouds; + private final Main main; + private final Sys sys; + private final Wind wind; /* Constructor */ - HourlyForecast(JSONObject jsonObj) { - super(jsonObj); + Forecast(JSONObject jsonObj) { + super(jsonObj); - JSONArray forecastArr = (jsonObj != null) ? jsonObj.optJSONArray(this.JSON_FORECAST_LIST) : new JSONArray(); - this.forecastList = (forecastArr != null) ? new ArrayList(forecastArr.length()) : Collections.EMPTY_LIST; - if (forecastArr != null && this.forecastList != Collections.EMPTY_LIST) { - for (int i = 0; i < forecastArr.length(); i++) { - JSONObject forecastObj = forecastArr.optJSONObject(i); - if (forecastObj != null) { - this.forecastList.add(new Forecast(forecastObj)); - } - } - } + this.dateTimeText = (jsonObj != null) ? jsonObj.optString(JSON_DT_TEXT, null) : null; + + JSONObject jsonObjClouds = (jsonObj != null) ? jsonObj.optJSONObject(JSON_CLOUDS) : null; + this.clouds = (jsonObjClouds != null) ? new Clouds(jsonObjClouds) : null; + + JSONObject jsonObjMain = (jsonObj != null) ? jsonObj.optJSONObject(JSON_MAIN) : null; + this.main = (jsonObjMain != null) ? new Main(jsonObjMain) : null; + + JSONObject jsonObjSys = (jsonObj != null) ? jsonObj.optJSONObject(JSON_SYS) : null; + this.sys = (jsonObjSys != null) ? new Sys(jsonObjSys) : null; + + JSONObject jsonObjWind = (jsonObj != null) ? jsonObj.optJSONObject(JSON_WIND) : null; + this.wind = (jsonObjWind != null) ? new Wind(jsonObjWind) : null; + } + + public boolean hasDateTimeText() { + return this.dateTimeText != null; } /** - * @param index Index of Forecast instance in the list. - * @return Forecast instance if available, otherwise null. + * @return true if Cloud instance is available, otherwise false. */ - public Forecast getForecastInstance(int index) { - return this.forecastList.get(index); + public boolean hasCloudsInstance() { + return clouds != null; + } + + /** + * @return true if Main instance is available, otherwise false. + */ + public boolean hasMainInstance() { + return main != null; + } + + /** + * @return true if System instance is available, otherwise false. + */ + public boolean hasSysInstance() { + return sys != null; + } + + /** + * @return true if Wind instance is available, otherwise false. + */ + public boolean hasWindInstance() { + return wind != null; + } + + public String getDateTimeText() { + return this.dateTimeText; + } + + /** + * @return Cloud instance if available, otherwise null. + */ + public Clouds getCloudsInstance() { + return this.clouds; + } + + /** + * @return Main instance if available, otherwise null. + */ + public Main getMainInstance() { + return this.main; + } + + /** + * @return System instance if available, otherwise null. + */ + public Sys getSysInstance() { + return this.sys; + } + + /** + * @return Wind instance if available, otherwise null. + */ + public Wind getWindInstance() { + return this.wind; } /** *

- * Parses forecast data (one element in the forecastList) and provides methods to get/access the same information. + * Parses clouds data and provides methods to get/access the same information. * This class provides has and get methods to access the information. *

*

@@ -98,281 +213,163 @@ public class HourlyForecast extends AbstractForecast { * Floating point: Not a number (NaN) * Others: null *

+ * + * @author Ashutosh Kumar Singh + * @version 2014/12/26 + * @since 2.5.0.1 */ - public static class Forecast extends AbstractForecast.Forecast { - /* - JSON Keys - */ - private static final String JSON_SYS = "sys"; - private static final String JSON_DT_TEXT = "dt_txt"; + public static class Clouds extends AbstractForecast.Forecast.Clouds { - /* - Instance Variables - */ - private final String dateTimeText; + Clouds() { + super(); + } - private final Clouds clouds; - private final Main main; - private final Sys sys; - private final Wind wind; - - /* - Constructor - */ - Forecast(JSONObject jsonObj) { - super(jsonObj); - - this.dateTimeText = (jsonObj != null) ? jsonObj.optString(JSON_DT_TEXT, null) : null; - - JSONObject jsonObjClouds = (jsonObj != null) ? jsonObj.optJSONObject(JSON_CLOUDS) : null; - this.clouds = (jsonObjClouds != null) ? new Clouds(jsonObjClouds) : null; - - JSONObject jsonObjMain = (jsonObj != null) ? jsonObj.optJSONObject(JSON_MAIN) : null; - this.main = (jsonObjMain != null) ? new Main(jsonObjMain) : null; - - JSONObject jsonObjSys = (jsonObj != null) ? jsonObj.optJSONObject(JSON_SYS) : null; - this.sys = (jsonObjSys != null) ? new Sys(jsonObjSys) : null; - - JSONObject jsonObjWind = (jsonObj != null) ? jsonObj.optJSONObject(JSON_WIND) : null; - this.wind = (jsonObjWind != null) ? new Wind(jsonObjWind) : null; - } - - public boolean hasDateTimeText() { - return this.dateTimeText != null; - } - - /** - * @return true if Clouds instance is available, otherwise false. - */ - public boolean hasCloudsInstance() { - return clouds != null; - } - - /** - * @return true if Main instance is available, otherwise false. - */ - public boolean hasMainInstance() { - return main != null; - } - - /** - * @return true if Sys instance is available, otherwise false. - */ - public boolean hasSysInstance() { - return sys != null; - } - - /** - * @return true if Wind instance is available, otherwise false. - */ - public boolean hasWindInstance() { - return wind != null; - } - - public String getDateTimeText() { - return this.dateTimeText; - } - - /** - * @return Clouds instance if available, otherwise null. - */ - public Clouds getCloudsInstance() { - return this.clouds; - } - - /** - * @return Main instance if available, otherwise null. - */ - public Main getMainInstance() { - return this.main; - } - - /** - * @return Sys instance if available, otherwise null. - */ - public Sys getSysInstance() { - return this.sys; - } - - /** - * @return Wind instance if available, otherwise null. - */ - public Wind getWindInstance() { - return this.wind; - } - - /** - *

- * Parses clouds 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/26 - * @since 2.5.0.1 - */ - public static class Clouds extends AbstractForecast.Forecast.Clouds { - - Clouds() { - super(); - } - - Clouds(JSONObject jsonObj) { - super(jsonObj); - } - } - - /** - *

- * Parses main 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/26 - * @since 2.5.0.1 - */ - public static class Main extends AbstractForecast.Forecast.Main { - private static final String JSON_MAIN_SEA_LEVEL = "sea_level"; - private static final String JSON_MAIN_GRND_LEVEL = "grnd_level"; - private static final String JSON_MAIN_TMP_KF = "temp_kf"; - - private final float seaLevel; - private final float groundLevel; - private final float tempKF; - - Main() { - super(); - - this.seaLevel = Float.NaN; - this.groundLevel = Float.NaN; - this.tempKF = Float.NaN; - } - - Main(JSONObject jsonObj) { - super(jsonObj); - - this.seaLevel = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_MAIN_SEA_LEVEL, Float.NaN) : Float.NaN; - this.groundLevel = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_MAIN_GRND_LEVEL, Float.NaN) : Float.NaN; - this.tempKF = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_MAIN_TMP_KF, Float.NaN) : Float.NaN; - } - - public boolean hasSeaLevel() { - return !Float.isNaN(this.seaLevel); - } - - public boolean hasGroundLevel() { - return !Float.isNaN(this.groundLevel); - } - - public boolean hasTempKF() { - return !Float.isNaN(this.tempKF); - } - - public float getSeaLevel() { - return this.seaLevel; - } - - public float getGroundLevel() { - return this.groundLevel; - } - - public float getTempKF() { - return this.tempKF; - } - } - - /** - *

- * Parses sys 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/26 - * @since 2.5.0.1 - */ - public static class Sys implements Serializable { - private static final String JSON_SYS_POD = "pod"; - - private final String pod; - - Sys() { - this.pod = null; - } - - Sys(JSONObject jsonObj) { - this.pod = (jsonObj != null) ? jsonObj.optString(JSON_SYS_POD, null) : null; - } - - public boolean hasPod() { - return this.pod != null && (! "".equals(this.pod)); - } - - public String getPod() { - return this.pod; - } - } - - /** - *

- * Parses wind 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/26 - * @since 2.5.0.1 - */ - public static class Wind extends AbstractWeather.Wind { - - Wind() { - super(); - } - - Wind(JSONObject jsonObj) { - super(jsonObj); - } - } + Clouds(JSONObject jsonObj) { + super(jsonObj); + } } + + /** + *

+ * Parses main 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/26 + * @since 2.5.0.1 + */ + public static class Main extends AbstractForecast.Forecast.Main { + private static final String JSON_MAIN_SEA_LEVEL = "sea_level"; + private static final String JSON_MAIN_GRND_LEVEL = "grnd_level"; + private static final String JSON_MAIN_TMP_KF = "temp_kf"; + + private final float seaLevel; + private final float groundLevel; + private final float tempKF; + + Main() { + super(); + + this.seaLevel = Float.NaN; + this.groundLevel = Float.NaN; + this.tempKF = Float.NaN; + } + + Main(JSONObject jsonObj) { + super(jsonObj); + + this.seaLevel = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_MAIN_SEA_LEVEL, Float.NaN) : Float.NaN; + this.groundLevel = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_MAIN_GRND_LEVEL, Float.NaN) : Float.NaN; + this.tempKF = (jsonObj != null) ? (float) jsonObj.optDouble(JSON_MAIN_TMP_KF, Float.NaN) : Float.NaN; + } + + public boolean hasSeaLevel() { + return !Float.isNaN(this.seaLevel); + } + + public boolean hasGroundLevel() { + return !Float.isNaN(this.groundLevel); + } + + public boolean hasTempKF() { + return !Float.isNaN(this.tempKF); + } + + public float getSeaLevel() { + return this.seaLevel; + } + + public float getGroundLevel() { + return this.groundLevel; + } + + public float getTempKF() { + return this.tempKF; + } + } + + /** + *

+ * Parses sys 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/26 + * @since 2.5.0.1 + */ + public static class Sys implements Serializable { + private static final String JSON_SYS_POD = "pod"; + + private final String pod; + + Sys() { + this.pod = null; + } + + Sys(JSONObject jsonObj) { + this.pod = (jsonObj != null) ? jsonObj.optString(JSON_SYS_POD, null) : null; + } + + public boolean hasPod() { + return this.pod != null && (!"".equals(this.pod)); + } + + public String getPod() { + return this.pod; + } + } + + /** + *

+ * Parses windData 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/26 + * @since 2.5.0.1 + */ + public static class Wind extends AbstractWeather.Wind { + + Wind() { + super(); + } + + Wind(JSONObject jsonObj) { + super(jsonObj); + } + } + } } diff --git a/src/main/java/net/aksingh/owmjapis/OpenWeatherMap.java b/src/main/java/net/aksingh/owmjapis/OpenWeatherMap.java index aa8ba21..8bee740 100644 --- a/src/main/java/net/aksingh/owmjapis/OpenWeatherMap.java +++ b/src/main/java/net/aksingh/owmjapis/OpenWeatherMap.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -36,746 +33,746 @@ import java.util.zip.InflaterInputStream; /** *

- * The starting point for all API operations. - * If you're new to this API, read the docs for this class first. + * The starting point for all API operations. + * If you're new to this API, read the docs for this class first. *

*

* Lets you access data from OpenWeatherMap.org using its Weather APIs. - * Henceforth, it's shortened as OWM.org to ease commenting. + * Henceforth, it's shortened as OpenWeatherMap.org to ease commenting. *

*

* Sample code:
- * OpenWeatherMap.org owm = new OpenWeatherMap("your-api-key");
- * OpenWeatherMap.org owm = new OpenWeatherMap(your-units, "your-api-key");
- * OpenWeatherMap.org owm = new OpenWeatherMap(your-units, your-language, "your-api-key"); + * OpenWeatherMap owm = new OpenWeatherMap("your-api-key");
+ * OpenWeatherMap owm = new OpenWeatherMap(your-units, "your-api-key");
+ * OpenWeatherMap owm = new OpenWeatherMap(your-units, your-language, "your-api-key"); *

* - * @author Ashutosh Kumar Singh + * @author Ashutosh Kumar Singh * @version 2015-01-17 - * @see OpenWeatherMap.org - * @see OpenWeatherMap.org API + * @see OpenWeatherMap.org + * @see OpenWeatherMap.org API * @since 2.5.0.1 */ public class OpenWeatherMap { - /* - URLs and parameters for OWM.org - */ - private static final String URL_API = "http://api.openweathermap.org/data/2.5/"; - private static final String URL_CURRENT = "weather?"; - private static final String URL_HOURLY_FORECAST = "forecast?"; - private static final String URL_DAILY_FORECAST = "forecast/daily?"; + /* + URLs and parameters for OpenWeatherMap.org + */ + private static final String URL_API = "https://api.openweathermap.org/data/2.5/"; + private static final String URL_CURRENT = "weather?"; + private static final String URL_HOURLY_FORECAST = "forecast?"; + private static final String URL_DAILY_FORECAST = "forecast/daily?"; - private static final String PARAM_COUNT = "cnt="; - private static final String PARAM_CITY_NAME = "q="; - private static final String PARAM_CITY_ID = "id="; - private static final String PARAM_LATITUDE = "lat="; - private static final String PARAM_LONGITUDE = "lon="; - private static final String PARAM_MODE = "mode="; - private static final String PARAM_UNITS = "units="; - private static final String PARAM_APPID = "appId="; - private static final String PARAM_LANG = "lang="; + private static final String PARAM_COUNT = "cnt="; + private static final String PARAM_CITY_NAME = "q="; + private static final String PARAM_CITY_ID = "id="; + private static final String PARAM_LATITUDE = "lat="; + private static final String PARAM_LONGITUDE = "lon="; + private static final String PARAM_MODE = "mode="; + private static final String PARAM_UNITS = "units="; + private static final String PARAM_APPID = "appId="; + private static final String PARAM_LANG = "lang="; - /* - Instance Variables - */ - private final OWMAddress owmAddress; - private final OWMResponse owmResponse; - private final OWMProxy owmProxy; + /* + Instance Variables + */ + private final OWMAddress owmAddress; + private final OWMResponse owmResponse; + private final OWMProxy owmProxy; - /** - * Constructor - * - * @param apiKey API key from OWM.org - * @see OWM.org API Key - */ - public OpenWeatherMap(String apiKey) { - this(Units.IMPERIAL, Language.ENGLISH, apiKey); - } + /** + * Constructor + * + * @param apiKey API key from OpenWeatherMap.org + * @see OpenWeatherMap.org API Key + */ + public OpenWeatherMap(String apiKey) { + this(Units.IMPERIAL, Language.ENGLISH, apiKey); + } - /** - * Constructor - * - * @param units Any constant from Units - * @param apiKey API key from OWM.org - * @see net.aksingh.owmjapis.OpenWeatherMap.Units - * @see OWM.org API Key - */ - public OpenWeatherMap(Units units, String apiKey) { - this(units, Language.ENGLISH, apiKey); - } + /** + * Constructor + * + * @param units Any constant from Units + * @param apiKey API key from OpenWeatherMap.org + * @see net.aksingh.owmjapis.OpenWeatherMap.Units + * @see OpenWeatherMap.org API Key + */ + public OpenWeatherMap(Units units, String apiKey) { + this(units, Language.ENGLISH, apiKey); + } - /** - * Constructor - * - * @param units Any constant from Units - * @param lang Any constant from Language - * @param apiKey API key from OWM.org - * @see net.aksingh.owmjapis.OpenWeatherMap.Units - * @see net.aksingh.owmjapis.OpenWeatherMap.Language - * @see OWM.org's Multilingual support - * @see OWM.org's API Key - */ - public OpenWeatherMap(Units units, Language lang, String apiKey) { - this.owmAddress = new OWMAddress(units, lang, apiKey); - this.owmProxy = new OWMProxy(null, Integer.MIN_VALUE, null, null); - this.owmResponse = new OWMResponse(owmAddress, owmProxy); - } + /** + * Constructor + * + * @param units Any constant from Units + * @param lang Any constant from Language + * @param apiKey API key from OpenWeatherMap.org + * @see net.aksingh.owmjapis.OpenWeatherMap.Units + * @see net.aksingh.owmjapis.OpenWeatherMap.Language + * @see [OpenWeatherMap.org's Multilingual support][https://openweathermap.org/current#multi] + * @see OpenWeatherMap.org's API Key + */ + public OpenWeatherMap(Units units, Language lang, String apiKey) { + this.owmAddress = new OWMAddress(units, lang, apiKey); + this.owmProxy = new OWMProxy(null, Integer.MIN_VALUE, null, null); + this.owmResponse = new OWMResponse(owmAddress, owmProxy); + } - /* - Getters - */ - public OWMAddress getOwmAddressInstance() { - return owmAddress; - } + /* + Getters + */ + public OWMAddress getOwmAddressInstance() { + return owmAddress; + } - public String getApiKey() { - return owmAddress.getAppId(); - } + public String getApiKey() { + return owmAddress.getAppId(); + } - public Units getUnits() { - return owmAddress.getUnits(); - } + public Units getUnits() { + return owmAddress.getUnits(); + } - public String getMode() { - return owmAddress.getMode(); - } + public String getMode() { + return owmAddress.getMode(); + } - public Language getLang() { - return owmAddress.getLang(); - } + public Language getLang() { + return owmAddress.getLang(); + } /* Setters */ - /** - * Set units for getting data from OWM.org - * - * @param units Any constant from Units - * @see net.aksingh.owmjapis.OpenWeatherMap.Units - */ - public void setUnits(Units units) { - owmAddress.setUnits(units); + /** + * Set units for getting data from OpenWeatherMap.org + * + * @param units Any constant from Units + * @see net.aksingh.owmjapis.OpenWeatherMap.Units + */ + public void setUnits(Units units) { + owmAddress.setUnits(units); + } + + /** + * Set API key for getting data from OpenWeatherMap.org + * + * @param appId API key from OpenWeatherMap.org + * @see OpenWeatherMap.org's API Key + */ + public void setApiKey(String appId) { + owmAddress.setAppId(appId); + } + + /** + * Set language for getting data from OpenWeatherMap.org + * + * @param lang Any constant from Language + * @see net.aksingh.owmjapis.OpenWeatherMap.Language + * @see [OpenWeatherMap.org's Multilingual support][https://openweathermap.org/current#multi] + */ + public void setLang(Language lang) { + owmAddress.setLang(lang); + } + + /** + * Set proxy for getting data from OpenWeatherMap.org + * + * @param ip IP address of the proxy + * @param port Port address of the proxy + */ + public void setProxy(String ip, int port) { + owmProxy.setIp(ip); + owmProxy.setPort(port); + owmProxy.setUser(null); + owmProxy.setPass(null); + } + + /** + * Set proxy and authentication details for getting data from OpenWeatherMap.org + * + * @param ip IP address of the proxy + * @param port Port address of the proxy + * @param user User name for the proxy if required + * @param pass Password for the proxy if required + */ + public void setProxy(String ip, int port, String user, String pass) { + owmProxy.setIp(ip); + owmProxy.setPort(port); + owmProxy.setUser(user); + owmProxy.setPass(pass); + } + + public CurrentWeather currentWeatherByCityName(String cityName) + throws IOException, JSONException { + String response = owmResponse.currentWeatherByCityName(cityName); + return this.currentWeatherFromRawResponse(response); + } + + public CurrentWeather currentWeatherByCityName(String cityName, String countryCode) + throws IOException, JSONException { + String response = owmResponse.currentWeatherByCityName(cityName, countryCode); + return this.currentWeatherFromRawResponse(response); + } + + public CurrentWeather currentWeatherByCityCode(long cityCode) + throws JSONException { + String response = owmResponse.currentWeatherByCityCode(cityCode); + return this.currentWeatherFromRawResponse(response); + } + + public CurrentWeather currentWeatherByCoordinates(float latitude, float longitude) + throws JSONException { + String response = owmResponse.currentWeatherByCoordinates(latitude, longitude); + return this.currentWeatherFromRawResponse(response); + } + + public CurrentWeather currentWeatherFromRawResponse(String response) + throws JSONException { + JSONObject jsonObj = (response != null) ? new JSONObject(response) : null; + return new CurrentWeather(jsonObj); + } + + public HourlyForecast hourlyForecastByCityName(String cityName) + throws IOException, JSONException { + String response = owmResponse.hourlyForecastByCityName(cityName); + return this.hourlyForecastFromRawResponse(response); + } + + public HourlyForecast hourlyForecastByCityName(String cityName, String countryCode) + throws IOException, JSONException { + String response = owmResponse.hourlyForecastByCityName(cityName, countryCode); + return this.hourlyForecastFromRawResponse(response); + } + + public HourlyForecast hourlyForecastByCityCode(long cityCode) + throws JSONException { + String response = owmResponse.hourlyForecastByCityCode(cityCode); + return this.hourlyForecastFromRawResponse(response); + } + + public HourlyForecast hourlyForecastByCoordinates(float latitude, float longitude) + throws JSONException { + String response = owmResponse.hourlyForecastByCoordinates(latitude, longitude); + return this.hourlyForecastFromRawResponse(response); + } + + public HourlyForecast hourlyForecastFromRawResponse(String response) + throws JSONException { + JSONObject jsonObj = (response != null) ? new JSONObject(response) : null; + return new HourlyForecast(jsonObj); + } + + public DailyForecast dailyForecastByCityName(String cityName, byte count) + throws IOException, JSONException { + String response = owmResponse.dailyForecastByCityName(cityName, count); + return this.dailyForecastFromRawResponse(response); + } + + public DailyForecast dailyForecastByCityName(String cityName, String countryCode, byte count) + throws IOException, JSONException { + String response = owmResponse.dailyForecastByCityName(cityName, countryCode, count); + return this.dailyForecastFromRawResponse(response); + } + + public DailyForecast dailyForecastByCityCode(long cityCode, byte count) + throws JSONException { + String response = owmResponse.dailyForecastByCityCode(cityCode, count); + return this.dailyForecastFromRawResponse(response); + } + + public DailyForecast dailyForecastByCoordinates(float latitude, float longitude, byte count) + throws JSONException { + String response = owmResponse.dailyForecastByCoordinates(latitude, longitude, count); + return this.dailyForecastFromRawResponse(response); + } + + public DailyForecast dailyForecastFromRawResponse(String response) + throws JSONException { + JSONObject jsonObj = (response != null) ? new JSONObject(response) : null; + return new DailyForecast(jsonObj); + } + + /** + * Units that can be set for getting data from OpenWeatherMap.org + * + * @since 2.5.0.3 + */ + public static enum Units { + METRIC("metric"), + IMPERIAL("imperial"); + + private final String unit; + + Units(String unit) { + this.unit = unit; + } + } + + /** + * Languages that can be set for getting data from OpenWeatherMap.org + * + * @since 2.5.0.3 + */ + public static enum Language { + ENGLISH("en"), + RUSSIAN("ru"), + ITALIAN("it"), + SPANISH("es"), + UKRAINIAN("uk"), + GERMAN("de"), + PORTUGUESE("pt"), + ROMANIAN("ro"), + POLISH("pl"), + FINNISH("fi"), + DUTCH("nl"), + FRENCH("FR"), + BULGARIAN("bg"), + SWEDISH("sv"), + CHINESE_TRADITIONAL("zh_tw"), + CHINESE_SIMPLIFIED("zh"), + TURKISH("tr"), + CROATIAN("hr"), + CATALAN("ca"); + + private final String lang; + + Language(String lang) { + this.lang = lang; + } + } + + /** + * Proxifies the default HTTP requests + * + * @since 2.5.0.5 + */ + private static class OWMProxy { + private String ip; + private int port; + private String user; + private String pass; + + private OWMProxy(String ip, int port, String user, String pass) { + this.ip = ip; + this.port = port; + this.user = user; + this.pass = pass; } - /** - * Set API key for getting data from OWM.org - * - * @param appId API key from OWM.org - * @see OWM.org's API Key - */ - public void setApiKey(String appId) { - owmAddress.setAppId(appId); + public Proxy getProxy() { + Proxy proxy = null; + + if (ip != null && (!"".equals(ip)) && port != Integer.MIN_VALUE) { + proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ip, port)); + } + + if (user != null && (!"".equals(user)) && pass != null && (!"".equals(pass))) { + Authenticator.setDefault(getAuthenticatorInstance(user, pass)); + } + + return proxy; } - /** - * Set language for getting data from OWM.org - * - * @param lang Any constant from Language - * @see net.aksingh.owmjapis.OpenWeatherMap.Language - * @see OWM.org's Multilingual support - */ - public void setLang(Language lang) { - owmAddress.setLang(lang); - } + private Authenticator getAuthenticatorInstance(final String user, final String pass) { + Authenticator authenticator = new Authenticator() { - /** - * Set proxy for getting data from OWM.org - * - * @param ip IP address of the proxy - * @param port Port address of the proxy - */ - public void setProxy(String ip, int port) { - owmProxy.setIp(ip); - owmProxy.setPort(port); - owmProxy.setUser(null); - owmProxy.setPass(null); - } - - /** - * Set proxy and authentication details for getting data from OWM.org - * - * @param ip IP address of the proxy - * @param port Port address of the proxy - * @param user User name for the proxy if required - * @param pass Password for the proxy if required - */ - public void setProxy(String ip, int port, String user, String pass) { - owmProxy.setIp(ip); - owmProxy.setPort(port); - owmProxy.setUser(user); - owmProxy.setPass(pass); - } - - public CurrentWeather currentWeatherByCityName(String cityName) - throws IOException, JSONException { - String response = owmResponse.currentWeatherByCityName(cityName); - return this.currentWeatherFromRawResponse(response); - } - - public CurrentWeather currentWeatherByCityName(String cityName, String countryCode) - throws IOException, JSONException { - String response = owmResponse.currentWeatherByCityName(cityName, countryCode); - return this.currentWeatherFromRawResponse(response); - } - - public CurrentWeather currentWeatherByCityCode(long cityCode) - throws JSONException { - String response = owmResponse.currentWeatherByCityCode(cityCode); - return this.currentWeatherFromRawResponse(response); - } - - public CurrentWeather currentWeatherByCoordinates(float latitude, float longitude) - throws JSONException { - String response = owmResponse.currentWeatherByCoordinates(latitude, longitude); - return this.currentWeatherFromRawResponse(response); - } - - public CurrentWeather currentWeatherFromRawResponse(String response) - throws JSONException { - JSONObject jsonObj = (response != null) ? new JSONObject(response) : null; - return new CurrentWeather(jsonObj); - } - - public HourlyForecast hourlyForecastByCityName(String cityName) - throws IOException, JSONException { - String response = owmResponse.hourlyForecastByCityName(cityName); - return this.hourlyForecastFromRawResponse(response); - } - - public HourlyForecast hourlyForecastByCityName(String cityName, String countryCode) - throws IOException, JSONException { - String response = owmResponse.hourlyForecastByCityName(cityName, countryCode); - return this.hourlyForecastFromRawResponse(response); - } - - public HourlyForecast hourlyForecastByCityCode(long cityCode) - throws JSONException { - String response = owmResponse.hourlyForecastByCityCode(cityCode); - return this.hourlyForecastFromRawResponse(response); - } - - public HourlyForecast hourlyForecastByCoordinates(float latitude, float longitude) - throws JSONException { - String response = owmResponse.hourlyForecastByCoordinates(latitude, longitude); - return this.hourlyForecastFromRawResponse(response); - } - - public HourlyForecast hourlyForecastFromRawResponse(String response) - throws JSONException { - JSONObject jsonObj = (response != null) ? new JSONObject(response) : null; - return new HourlyForecast(jsonObj); - } - - public DailyForecast dailyForecastByCityName(String cityName, byte count) - throws IOException, JSONException { - String response = owmResponse.dailyForecastByCityName(cityName, count); - return this.dailyForecastFromRawResponse(response); - } - - public DailyForecast dailyForecastByCityName(String cityName, String countryCode, byte count) - throws IOException, JSONException { - String response = owmResponse.dailyForecastByCityName(cityName, countryCode, count); - return this.dailyForecastFromRawResponse(response); - } - - public DailyForecast dailyForecastByCityCode(long cityCode, byte count) - throws JSONException { - String response = owmResponse.dailyForecastByCityCode(cityCode, count); - return this.dailyForecastFromRawResponse(response); - } - - public DailyForecast dailyForecastByCoordinates(float latitude, float longitude, byte count) - throws JSONException { - String response = owmResponse.dailyForecastByCoordinates(latitude, longitude, count); - return this.dailyForecastFromRawResponse(response); - } - - public DailyForecast dailyForecastFromRawResponse(String response) - throws JSONException { - JSONObject jsonObj = (response != null) ? new JSONObject(response) : null; - return new DailyForecast(jsonObj); - } - - /** - * Units that can be set for getting data from OWM.org - * - * @since 2.5.0.3 - */ - public static enum Units { - METRIC("metric"), - IMPERIAL("imperial"); - - private final String unit; - - Units(String unit) { - this.unit = unit; + public PasswordAuthentication getPasswordAuthentication() { + return (new PasswordAuthentication(user, pass.toCharArray())); } + }; + + return authenticator; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public void setPort(int port) { + this.port = port; + } + + public void setUser(String user) { + this.user = user; + } + + public void setPass(String pass) { + this.pass = pass; + } + } + + /** + * Generates addresses for accessing the information from OpenWeatherMap.org + * + * @since 2.5.0.3 + */ + public static class OWMAddress { + private static final String MODE = "json"; + private static final String ENCODING = "UTF-8"; + + private String mode; + private Units units; + private String appId; + private Language lang; + + /* + Constructors + */ + private OWMAddress(String appId) { + this(Units.IMPERIAL, Language.ENGLISH, appId); + } + + private OWMAddress(Units units, String appId) { + this(units, Language.ENGLISH, appId); + } + + private OWMAddress(Units units, Language lang, String appId) { + this.mode = MODE; + this.units = units; + this.lang = lang; + this.appId = appId; + } + + /* + Getters + */ + private String getAppId() { + return this.appId; + } + + private Units getUnits() { + return this.units; + } + + private String getMode() { + return this.mode; + } + + private Language getLang() { + return this.lang; + } + + /* + Setters + */ + private void setUnits(Units units) { + this.units = units; + } + + private void setAppId(String appId) { + this.appId = appId; + } + + private void setLang(Language lang) { + this.lang = lang; + } + + /* + Addresses for current weather + */ + public String currentWeatherByCityName(String cityName) throws UnsupportedEncodingException { + return new StringBuilder() + .append(URL_API).append(URL_CURRENT) + .append(PARAM_CITY_NAME).append(URLEncoder.encode(cityName, ENCODING)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + public String currentWeatherByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { + return currentWeatherByCityName(new StringBuilder() + .append(cityName).append(",").append(countryCode) + .toString()); + } + + public String currentWeatherByCityCode(long cityCode) { + return new StringBuilder() + .append(URL_API).append(URL_CURRENT) + .append(PARAM_CITY_ID).append(Long.toString(cityCode)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + public String currentWeatherByCoordinates(float latitude, float longitude) { + return new StringBuilder() + .append(URL_API).append(URL_CURRENT) + .append(PARAM_LATITUDE).append(Float.toString(latitude)).append("&") + .append(PARAM_LONGITUDE).append(Float.toString(longitude)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + /* + Addresses for hourly forecasts + */ + public String hourlyForecastByCityName(String cityName) throws UnsupportedEncodingException { + return new StringBuilder() + .append(URL_API).append(URL_HOURLY_FORECAST) + .append(PARAM_CITY_NAME).append(URLEncoder.encode(cityName, ENCODING)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + public String hourlyForecastByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { + return hourlyForecastByCityName(new StringBuilder() + .append(cityName).append(",").append(countryCode) + .toString()); + } + + public String hourlyForecastByCityCode(long cityCode) { + return new StringBuilder() + .append(URL_API).append(URL_HOURLY_FORECAST) + .append(PARAM_CITY_ID).append(Long.toString(cityCode)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + public String hourlyForecastByCoordinates(float latitude, float longitude) { + return new StringBuilder() + .append(URL_API).append(URL_HOURLY_FORECAST) + .append(PARAM_LATITUDE).append(Float.toString(latitude)).append("&") + .append(PARAM_LONGITUDE).append(Float.toString(longitude)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + /* + Addresses for daily forecasts + */ + public String dailyForecastByCityName(String cityName, byte count) throws UnsupportedEncodingException { + return new StringBuilder() + .append(URL_API).append(URL_DAILY_FORECAST) + .append(PARAM_CITY_NAME).append(URLEncoder.encode(cityName, ENCODING)).append("&") + .append(PARAM_COUNT).append(Byte.toString(count)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + public String dailyForecastByCityName(String cityName, String countryCode, byte count) throws UnsupportedEncodingException { + return dailyForecastByCityName(new StringBuilder() + .append(cityName).append(",").append(countryCode) + .toString(), count); + } + + public String dailyForecastByCityCode(long cityCode, byte count) { + return new StringBuilder() + .append(URL_API).append(URL_DAILY_FORECAST) + .append(PARAM_CITY_ID).append(Long.toString(cityCode)).append("&") + .append(PARAM_COUNT).append(Byte.toString(count)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + + public String dailyForecastByCoordinates(float latitude, float longitude, byte count) { + return new StringBuilder() + .append(URL_API).append(URL_DAILY_FORECAST) + .append(PARAM_LATITUDE).append(Float.toString(latitude)).append("&") + .append(PARAM_LONGITUDE).append(Float.toString(longitude)).append("&") + .append(PARAM_COUNT).append(Byte.toString(count)).append("&") + .append(PARAM_MODE).append(this.mode).append("&") + .append(PARAM_UNITS).append(this.units).append("&") + .append(PARAM_LANG).append(this.lang).append("&") + .append(PARAM_APPID).append(this.appId) + .toString(); + } + } + + /** + * Requests OpenWeatherMap.org for data and provides back the incoming response. + * + * @since 2.5.0.3 + */ + private static class OWMResponse { + private final OWMAddress owmAddress; + private final OWMProxy owmProxy; + + public OWMResponse(OWMAddress owmAddress, OWMProxy owmProxy) { + this.owmAddress = owmAddress; + this.owmProxy = owmProxy; + } + + /* + Responses for current weather + */ + public String currentWeatherByCityName(String cityName) throws UnsupportedEncodingException { + String address = owmAddress.currentWeatherByCityName(cityName); + return httpGET(address); + } + + public String currentWeatherByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { + String address = owmAddress.currentWeatherByCityName(cityName, countryCode); + return httpGET(address); + } + + public String currentWeatherByCityCode(long cityCode) { + String address = owmAddress.currentWeatherByCityCode(cityCode); + return httpGET(address); + } + + public String currentWeatherByCoordinates(float latitude, float longitude) { + String address = owmAddress.currentWeatherByCoordinates(latitude, longitude); + return httpGET(address); + } + + /* + Responses for hourly forecasts + */ + public String hourlyForecastByCityName(String cityName) throws UnsupportedEncodingException { + String address = owmAddress.hourlyForecastByCityName(cityName); + return httpGET(address); + } + + public String hourlyForecastByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { + String address = owmAddress.hourlyForecastByCityName(cityName, countryCode); + return httpGET(address); + } + + public String hourlyForecastByCityCode(long cityCode) { + String address = owmAddress.hourlyForecastByCityCode(cityCode); + return httpGET(address); + } + + public String hourlyForecastByCoordinates(float latitude, float longitude) { + String address = owmAddress.hourlyForecastByCoordinates(latitude, longitude); + return httpGET(address); + } + + /* + Responses for daily forecasts + */ + public String dailyForecastByCityName(String cityName, byte count) throws UnsupportedEncodingException { + String address = owmAddress.dailyForecastByCityName(cityName, count); + return httpGET(address); + } + + public String dailyForecastByCityName(String cityName, String countryCode, byte count) throws UnsupportedEncodingException { + String address = owmAddress.dailyForecastByCityName(cityName, countryCode, count); + return httpGET(address); + } + + public String dailyForecastByCityCode(long cityCode, byte count) { + String address = owmAddress.dailyForecastByCityCode(cityCode, count); + return httpGET(address); + } + + public String dailyForecastByCoordinates(float latitude, float longitude, byte count) { + String address = owmAddress.dailyForecastByCoordinates(latitude, longitude, count); + return httpGET(address); } /** - * Languages that can be set for getting data from OWM.org + * Implements HTTP's GET method * - * @since 2.5.0.3 + * @param requestAddress Address to be loaded + * @return Response if successful, else null + * @see HTTP - (9.3) GET */ - public static enum Language { - ENGLISH("en"), - RUSSIAN("ru"), - ITALIAN("it"), - SPANISH("es"), - UKRAINIAN("uk"), - GERMAN("de"), - PORTUGUESE("pt"), - ROMANIAN("ro"), - POLISH("pl"), - FINNISH("fi"), - DUTCH("nl"), - FRENCH("FR"), - BULGARIAN("bg"), - SWEDISH("sv"), - CHINESE_TRADITIONAL("zh_tw"), - CHINESE_SIMPLIFIED("zh"), - TURKISH("tr"), - CROATIAN("hr"), - CATALAN("ca"); + private String httpGET(String requestAddress) { + URL request; + HttpURLConnection connection = null; + BufferedReader reader = null; - private final String lang; + String tmpStr; + String response = null; - Language(String lang) { - this.lang = lang; - } - } + try { + request = new URL(requestAddress); - /** - * Proxifies the default HTTP requests - * - * @since 2.5.0.5 - */ - private static class OWMProxy { - private String ip; - private int port; - private String user; - private String pass; - - private OWMProxy(String ip, int port, String user, String pass) { - this.ip = ip; - this.port = port; - this.user = user; - this.pass = pass; + if (owmProxy.getProxy() != null) { + connection = (HttpURLConnection) request.openConnection(owmProxy.getProxy()); + } else { + connection = (HttpURLConnection) request.openConnection(); } - public Proxy getProxy() { - Proxy proxy = null; + connection.setRequestMethod("GET"); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(false); + connection.setRequestProperty("Accept-Encoding", "gzip, deflate"); + connection.connect(); - if (ip != null && (! "".equals(ip)) && port != Integer.MIN_VALUE) { - proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ip, port)); + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + String encoding = connection.getContentEncoding(); + + try { + if (encoding != null && "gzip".equalsIgnoreCase(encoding)) { + reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(connection.getInputStream()))); + } else if (encoding != null && "deflate".equalsIgnoreCase(encoding)) { + reader = new BufferedReader(new InputStreamReader(new InflaterInputStream(connection.getInputStream(), new Inflater(true)))); + } else { + reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); } - if (user != null && (! "".equals(user)) && pass != null && (! "".equals(pass))) { - Authenticator.setDefault(getAuthenticatorInstance(user, pass)); + while ((tmpStr = reader.readLine()) != null) { + response = tmpStr; } - - return proxy; - } - - private Authenticator getAuthenticatorInstance(final String user, final String pass) { - Authenticator authenticator = new Authenticator() { - - public PasswordAuthentication getPasswordAuthentication() { - return (new PasswordAuthentication(user, pass.toCharArray())); - } - }; - - return authenticator; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public void setPort(int port) { - this.port = port; - } - - public void setUser(String user) { - this.user = user; - } - - public void setPass(String pass) { - this.pass = pass; - } - } - - /** - * Generates addresses for accessing the information from OWM.org - * - * @since 2.5.0.3 - */ - public static class OWMAddress { - private static final String MODE = "json"; - private static final String ENCODING = "UTF-8"; - - private String mode; - private Units units; - private String appId; - private Language lang; - - /* - Constructors - */ - private OWMAddress(String appId) { - this(Units.IMPERIAL, Language.ENGLISH, appId); - } - - private OWMAddress(Units units, String appId) { - this(units, Language.ENGLISH, appId); - } - - private OWMAddress(Units units, Language lang, String appId) { - this.mode = MODE; - this.units = units; - this.lang = lang; - this.appId = appId; - } - - /* - Getters - */ - private String getAppId() { - return this.appId; - } - - private Units getUnits() { - return this.units; - } - - private String getMode() { - return this.mode; - } - - private Language getLang() { - return this.lang; - } - - /* - Setters - */ - private void setUnits(Units units) { - this.units = units; - } - - private void setAppId(String appId) { - this.appId = appId; - } - - private void setLang(Language lang) { - this.lang = lang; - } - - /* - Addresses for current weather - */ - public String currentWeatherByCityName(String cityName) throws UnsupportedEncodingException { - return new StringBuilder() - .append(URL_API).append(URL_CURRENT) - .append(PARAM_CITY_NAME).append(URLEncoder.encode(cityName, ENCODING)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - public String currentWeatherByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { - return currentWeatherByCityName(new StringBuilder() - .append(cityName).append(",").append(countryCode) - .toString()); - } - - public String currentWeatherByCityCode(long cityCode) { - return new StringBuilder() - .append(URL_API).append(URL_CURRENT) - .append(PARAM_CITY_ID).append(Long.toString(cityCode)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - public String currentWeatherByCoordinates(float latitude, float longitude) { - return new StringBuilder() - .append(URL_API).append(URL_CURRENT) - .append(PARAM_LATITUDE).append(Float.toString(latitude)).append("&") - .append(PARAM_LONGITUDE).append(Float.toString(longitude)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - /* - Addresses for hourly forecasts - */ - public String hourlyForecastByCityName(String cityName) throws UnsupportedEncodingException { - return new StringBuilder() - .append(URL_API).append(URL_HOURLY_FORECAST) - .append(PARAM_CITY_NAME).append(URLEncoder.encode(cityName, ENCODING)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - public String hourlyForecastByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { - return hourlyForecastByCityName(new StringBuilder() - .append(cityName).append(",").append(countryCode) - .toString()); - } - - public String hourlyForecastByCityCode(long cityCode) { - return new StringBuilder() - .append(URL_API).append(URL_HOURLY_FORECAST) - .append(PARAM_CITY_ID).append(Long.toString(cityCode)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - public String hourlyForecastByCoordinates(float latitude, float longitude) { - return new StringBuilder() - .append(URL_API).append(URL_HOURLY_FORECAST) - .append(PARAM_LATITUDE).append(Float.toString(latitude)).append("&") - .append(PARAM_LONGITUDE).append(Float.toString(longitude)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - /* - Addresses for daily forecasts - */ - public String dailyForecastByCityName(String cityName, byte count) throws UnsupportedEncodingException { - return new StringBuilder() - .append(URL_API).append(URL_DAILY_FORECAST) - .append(PARAM_CITY_NAME).append(URLEncoder.encode(cityName, ENCODING)).append("&") - .append(PARAM_COUNT).append(Byte.toString(count)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - public String dailyForecastByCityName(String cityName, String countryCode, byte count) throws UnsupportedEncodingException { - return dailyForecastByCityName(new StringBuilder() - .append(cityName).append(",").append(countryCode) - .toString(), count); - } - - public String dailyForecastByCityCode(long cityCode, byte count) { - return new StringBuilder() - .append(URL_API).append(URL_DAILY_FORECAST) - .append(PARAM_CITY_ID).append(Long.toString(cityCode)).append("&") - .append(PARAM_COUNT).append(Byte.toString(count)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - - public String dailyForecastByCoordinates(float latitude, float longitude, byte count) { - return new StringBuilder() - .append(URL_API).append(URL_DAILY_FORECAST) - .append(PARAM_LATITUDE).append(Float.toString(latitude)).append("&") - .append(PARAM_LONGITUDE).append(Float.toString(longitude)).append("&") - .append(PARAM_COUNT).append(Byte.toString(count)).append("&") - .append(PARAM_MODE).append(this.mode).append("&") - .append(PARAM_UNITS).append(this.units).append("&") - .append(PARAM_LANG).append(this.lang).append("&") - .append(PARAM_APPID).append(this.appId) - .toString(); - } - } - - /** - * Requests OWM.org for data and provides back the incoming response. - * - * @since 2.5.0.3 - */ - private static class OWMResponse { - private final OWMAddress owmAddress; - private final OWMProxy owmProxy; - - public OWMResponse(OWMAddress owmAddress, OWMProxy owmProxy) { - this.owmAddress = owmAddress; - this.owmProxy = owmProxy; - } - - /* - Responses for current weather - */ - public String currentWeatherByCityName(String cityName) throws UnsupportedEncodingException { - String address = owmAddress.currentWeatherByCityName(cityName); - return httpGET(address); - } - - public String currentWeatherByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { - String address = owmAddress.currentWeatherByCityName(cityName, countryCode); - return httpGET(address); - } - - public String currentWeatherByCityCode(long cityCode) { - String address = owmAddress.currentWeatherByCityCode(cityCode); - return httpGET(address); - } - - public String currentWeatherByCoordinates(float latitude, float longitude) { - String address = owmAddress.currentWeatherByCoordinates(latitude, longitude); - return httpGET(address); - } - - /* - Responses for hourly forecasts - */ - public String hourlyForecastByCityName(String cityName) throws UnsupportedEncodingException { - String address = owmAddress.hourlyForecastByCityName(cityName); - return httpGET(address); - } - - public String hourlyForecastByCityName(String cityName, String countryCode) throws UnsupportedEncodingException { - String address = owmAddress.hourlyForecastByCityName(cityName, countryCode); - return httpGET(address); - } - - public String hourlyForecastByCityCode(long cityCode) { - String address = owmAddress.hourlyForecastByCityCode(cityCode); - return httpGET(address); - } - - public String hourlyForecastByCoordinates(float latitude, float longitude) { - String address = owmAddress.hourlyForecastByCoordinates(latitude, longitude); - return httpGET(address); - } - - /* - Responses for daily forecasts - */ - public String dailyForecastByCityName(String cityName, byte count) throws UnsupportedEncodingException { - String address = owmAddress.dailyForecastByCityName(cityName, count); - return httpGET(address); - } - - public String dailyForecastByCityName(String cityName, String countryCode, byte count) throws UnsupportedEncodingException { - String address = owmAddress.dailyForecastByCityName(cityName, countryCode, count); - return httpGET(address); - } - - public String dailyForecastByCityCode(long cityCode, byte count) { - String address = owmAddress.dailyForecastByCityCode(cityCode, count); - return httpGET(address); - } - - public String dailyForecastByCoordinates(float latitude, float longitude, byte count) { - String address = owmAddress.dailyForecastByCoordinates(latitude, longitude, count); - return httpGET(address); - } - - /** - * Implements HTTP's GET method - * - * @param requestAddress Address to be loaded - * @return Response if successful, else null - * @see HTTP - (9.3) GET - */ - private String httpGET(String requestAddress) { - URL request; - HttpURLConnection connection = null; - BufferedReader reader = null; - - String tmpStr; - String response = null; - - try { - request = new URL(requestAddress); - - if (owmProxy.getProxy() != null) { - connection = (HttpURLConnection) request.openConnection(owmProxy.getProxy()); - } else { - connection = (HttpURLConnection) request.openConnection(); - } - - connection.setRequestMethod("GET"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(false); - connection.setRequestProperty("Accept-Encoding", "gzip, deflate"); - connection.connect(); - - if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { - String encoding = connection.getContentEncoding(); - - try { - if (encoding != null && "gzip".equalsIgnoreCase(encoding)) { - reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(connection.getInputStream()))); - } else if (encoding != null && "deflate".equalsIgnoreCase(encoding)) { - reader = new BufferedReader(new InputStreamReader(new InflaterInputStream(connection.getInputStream(), new Inflater(true)))); - } else { - reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); - } - - while ((tmpStr = reader.readLine()) != null) { - response = tmpStr; - } - } catch (IOException e) { - System.err.println("Error: " + e.getMessage()); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - System.err.println("Error: " + e.getMessage()); - } - } - } - } else { // if HttpURLConnection is not okay - try { - reader = new BufferedReader(new InputStreamReader(connection.getErrorStream())); - while ((tmpStr = reader.readLine()) != null) { - response = tmpStr; - } - } catch (IOException e) { - System.err.println("Error: " + e.getMessage()); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - System.err.println("Error: " + e.getMessage()); - } - } - } - - // if response is bad - System.err.println("Bad Response: " + response + "\n"); - return null; - } - } catch (IOException e) { + } catch (IOException e) { + System.err.println("Error: " + e.getMessage()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { System.err.println("Error: " + e.getMessage()); - response = null; - } finally { - if (connection != null) { - connection.disconnect(); - } + } } + } + } else { // if HttpURLConnection is not okay + try { + reader = new BufferedReader(new InputStreamReader(connection.getErrorStream())); + while ((tmpStr = reader.readLine()) != null) { + response = tmpStr; + } + } catch (IOException e) { + System.err.println("Error: " + e.getMessage()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + System.err.println("Error: " + e.getMessage()); + } + } + } - return response; + // if response is bad + System.err.println("Bad Response: " + response + "\n"); + return null; } + } catch (IOException e) { + System.err.println("Error: " + e.getMessage()); + response = null; + } finally { + if (connection != null) { + connection.disconnect(); + } + } + + return response; } + } } diff --git a/src/main/java/net/aksingh/owmjapis/Tools.java b/src/main/java/net/aksingh/owmjapis/Tools.java index dc5430e..90c0f9d 100644 --- a/src/main/java/net/aksingh/owmjapis/Tools.java +++ b/src/main/java/net/aksingh/owmjapis/Tools.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -32,60 +29,63 @@ package net.aksingh.owmjapis; * @since 2.5.0.1 */ public class Tools { - /** - *

- * Converts degree to direction. - *

- * - * @param degree Degree of wind as received from OWM.org - * @return Direction - * @throws IllegalArgumentException Degree should be between 0 and 360. - */ - public String convertDegree2Direction(float degree) - throws IllegalArgumentException { - String direction; + /** + *

+ * Converts degree to direction. + *

+ * + * @param degree Degree of wind as received from OpenWeatherMap.org + * @return Direction + * @throws IllegalArgumentException Degree should be between 0 and 360. + * @deprecated As of version 2.5.1 and will be removed in version 2.5.2, + * use {@link net.aksingh.owmjapis.util.ConversionTools.convertDegree2DirectionCode} instead. + */ + @Deprecated + public String convertDegree2Direction(float degree) + throws IllegalArgumentException { + String direction; - // degree should be between 0 and 360 - if ((degree < 0.0f) || (degree > 360.0f)) { - throw new IllegalArgumentException("Degree cannot be less than 0 or more than 360."); - } - - if (degree <= 11.25f) { - direction = "N"; - } else if (degree <= 33.75f) { - direction = "NNE"; - } else if (degree <= 56.25f) { - direction = "NE"; - } else if (degree <= 78.75f) { - direction = "ENE"; - } else if (degree <= 101.25f) { - direction = "E"; - } else if (degree <= 123.75f) { - direction = "ESE"; - } else if (degree <= 146.25f) { - direction = "SE"; - } else if (degree <= 168.75f) { - direction = "SSE"; - } else if (degree <= 191.25f) { - direction = "S"; - } else if (degree <= 213.75f) { - direction = "SSW"; - } else if (degree <= 236.25f) { - direction = "SW"; - } else if (degree <= 258.75f) { - direction = "WSW"; - } else if (degree <= 281.25f) { - direction = "W"; - } else if (degree <= 303.75f) { - direction = "WNW"; - } else if (degree <= 326.25f) { - direction = "NW"; - } else if (degree <= 348.75f) { - direction = "NNW"; - } else { - direction = "N"; - } - - return direction; + // degree should be between 0 and 360 + if ((degree < 0.0f) || (degree > 360.0f)) { + throw new IllegalArgumentException("Degree cannot be less than 0 or more than 360."); } + + if (degree <= 11.25f) { + direction = "N"; + } else if (degree <= 33.75f) { + direction = "NNE"; + } else if (degree <= 56.25f) { + direction = "NE"; + } else if (degree <= 78.75f) { + direction = "ENE"; + } else if (degree <= 101.25f) { + direction = "E"; + } else if (degree <= 123.75f) { + direction = "ESE"; + } else if (degree <= 146.25f) { + direction = "SE"; + } else if (degree <= 168.75f) { + direction = "SSE"; + } else if (degree <= 191.25f) { + direction = "S"; + } else if (degree <= 213.75f) { + direction = "SSW"; + } else if (degree <= 236.25f) { + direction = "SW"; + } else if (degree <= 258.75f) { + direction = "WSW"; + } else if (degree <= 281.25f) { + direction = "W"; + } else if (degree <= 303.75f) { + direction = "WNW"; + } else if (degree <= 326.25f) { + direction = "NW"; + } else if (degree <= 348.75f) { + direction = "NNW"; + } else { + direction = "N"; + } + + return direction; + } } diff --git a/src/main/kotlin/net/aksingh/owmjapis/api/APIException.kt b/src/main/kotlin/net/aksingh/owmjapis/api/APIException.kt new file mode 100644 index 0000000..10c9c46 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/api/APIException.kt @@ -0,0 +1,30 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.api + +class APIException(val code: Int, val info: String) : Exception() { + + override val message: String? + get() = createMessage() + + fun createMessage(): String { + return "API call gave error: " + code + " - " + info + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/api/CurrentWeatherAPI.kt b/src/main/kotlin/net/aksingh/owmjapis/api/CurrentWeatherAPI.kt new file mode 100644 index 0000000..e39d626 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/api/CurrentWeatherAPI.kt @@ -0,0 +1,67 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.api + +import net.aksingh.owmjapis.model.CurrentWeather +import net.aksingh.owmjapis.model.CurrentWeatherList +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface CurrentWeatherAPI { + + @GET("weather") + fun getCurrentWeatherByCityName( + @Query("q") name: String + ): Call + + @GET("weather") + fun getCurrentWeatherByCityId( + @Query("id") id: Int + ): Call + + @GET("weather") + fun getCurrentWeatherByCoords( + @Query("lat") lat: Float, + @Query("lon") lon: Float + ): Call + + @GET("weather") + fun getCurrentWeatherByZipCode( + @Query("zip") zip: String + ): Call + + @GET("weather") + fun getCurrentWeatherListInZone( + @Query("bbox") box: String + ): Call + + @GET("weather") + fun getCurrentWeatherListInCycle( + @Query("lat") lat: Float, + @Query("lon") lon: Float, + @Query("cnt") count: Short + ): Call + + @GET("weather") + fun getCurrentWeatherListByCityId( + @Query("id") ids: String + ): Call +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/api/DailyForecastAPI.kt b/src/main/kotlin/net/aksingh/owmjapis/api/DailyForecastAPI.kt new file mode 100644 index 0000000..9fbe8a0 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/api/DailyForecastAPI.kt @@ -0,0 +1,53 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.api + +import net.aksingh.owmjapis.model.DailyForecast +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface DailyForecastAPI { + + @GET("forecast/daily") + fun getDailyForecastByCityName( + @Query("q") name: String, + @Query("cnt") count: Byte + ): Call + + @GET("forecast/daily") + fun getDailyForecastByCityId( + @Query("id") id: Int, + @Query("cnt") count: Byte + ): Call + + @GET("forecast/daily") + fun getDailyForecastByCoords( + @Query("lat") lat: Float, + @Query("lon") lon: Float, + @Query("cnt") count: Byte + ): Call + + @GET("forecast/daily") + fun getDailyForecastByZipCode( + @Query("zip") zip: String, + @Query("cnt") count: Byte + ): Call +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/api/HourlyForecastAPI.kt b/src/main/kotlin/net/aksingh/owmjapis/api/HourlyForecastAPI.kt new file mode 100644 index 0000000..caf9921 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/api/HourlyForecastAPI.kt @@ -0,0 +1,49 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.api + +import net.aksingh.owmjapis.model.HourlyForecast +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface HourlyForecastAPI { + + @GET("forecast") + fun getHourlyForecastByCityName( + @Query("q") name: String + ): Call + + @GET("forecast") + fun getHourlyForecastByCityId( + @Query("id") id: Int + ): Call + + @GET("forecast") + fun getHourlyForecastByCoords( + @Query("lat") lat: Float, + @Query("lon") lon: Float + ): Call + + @GET("forecast") + fun getHourlyForecastByZipCode( + @Query("zip") zip: String + ): Call +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/core/OWM.kt b/src/main/kotlin/net/aksingh/owmjapis/core/OWM.kt new file mode 100644 index 0000000..9fdf013 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/core/OWM.kt @@ -0,0 +1,873 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.core + +import com.google.gson.GsonBuilder +import net.aksingh.owmjapis.api.APIException +import net.aksingh.owmjapis.api.CurrentWeatherAPI +import net.aksingh.owmjapis.api.DailyForecastAPI +import net.aksingh.owmjapis.api.HourlyForecastAPI +import net.aksingh.owmjapis.model.CurrentWeather +import net.aksingh.owmjapis.model.DailyForecast +import net.aksingh.owmjapis.model.HourlyForecast +import net.aksingh.owmjapis.util.OkHttpTools +import net.aksingh.owmjapis.util.SystemTools +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.net.InetSocketAddress +import java.net.Proxy + + +/** + * **Starting point for this lib.** If you're new to this API, start from this class. + * + * Lets you access data from OpenWeatherMap.org using its Weather APIs. + * + * **Sample code in Java:** + * `OWM owm = new OWM("your-api-key");` + * + * **Sample code in Kotlin:** + * `owm: OWM = OWM("your-api-key")` + * + * [OpenWeatherMap.org](https://openweathermap.org/) + * [OpenWeatherMap.org APIs](https://openweathermap.org/api/) + * + * @author Ashutosh Kumar Singh + * @version 2017-11-07 + * + * @since 2.5.1.0 + */ +class OWM { + + private val OWM_25_BASE_URL: String = "https://api.openweathermap.org/data/2.5/" + private val OWM_25_DAILY_FORECAST_MAX_COUNT: Byte = 16 + + private var apiKey: String + set(value) { + if (value.isEmpty() || value.isBlank()) { + throw IllegalArgumentException("API key can't be empty/blank. Kindly get an API key from OpenWeatherMap.org") + } + + field = value + } + + private var accuracy: OWM.Accuracy + private var unit: OWM.Unit + private var lang: OWM.Language + private var proxy: Proxy + + private var retrofit: Retrofit + + /** + * Constructor + * + * Defaults: Search accuracy is set to like + * Defaults: Unit is set to standard + * Defaults: Language is set to English + * Defaults: Proxy is set to system's proxy + * + * [OpenWeatherMap.org API key](https://openweathermap.org/appid) + * + * @param apiKey API key from OpenWeatherMap.org + */ + constructor(apiKey: String) { + this.apiKey = apiKey + + this.accuracy = OWM.Accuracy.LIKE + this.unit = OWM.Unit.STANDARD + this.lang = OWM.Language.ENGLISH + this.proxy = SystemTools.getSystemProxy() + + this.retrofit = createRetrofitInstance(this.proxy) + } + + /** + * Set search accuracy for getting data from OpenWeatherMap.org + * + * @param accuracy Search accuracy + */ + fun setAccuracy(accuracy: OWM.Accuracy): OWM { + this.accuracy = accuracy + this.retrofit = createRetrofitInstance(this.proxy) + + return this + } + + /** + * Set unit for getting data from OpenWeatherMap.org + * + * @param unit Unit + */ + fun setUnit(unit: OWM.Unit): OWM { + this.unit = unit + this.retrofit = createRetrofitInstance(this.proxy) + + return this + } + + /** + * Set language for getting data from OpenWeatherMap.org + * + * @param lang Language + */ + fun setLanguage(lang: OWM.Language): OWM { + this.lang = lang + this.retrofit = createRetrofitInstance(this.proxy) + + return this + } + + /** + * Set proxy for getting data from OpenWeatherMap.org + * + * @param proxy Proxy + */ + fun setProxy(proxy: Proxy): OWM { + this.retrofit = createRetrofitInstance(proxy) + + return this + } + + /** + * Set authenticated proxy for getting data from OpenWeatherMap.org + * + * @param proxy Proxy + * @param user User name for the proxy + * @param pass Password for the proxy + */ + fun setProxy(proxy: Proxy, user: String, pass: String): OWM { + setProxy(proxy) + SystemTools.setProxyAuthDetails(user, pass) + + return this + } + + /** + * Set HTTP proxy for getting data from OpenWeatherMap.org + * + * @param host Host address of the proxy + * @param port Port address of the proxy + */ + fun setProxy(host: String, port: Int): OWM { + setProxy(host, port, Proxy.Type.HTTP) + + return this + } + + /** + * Set proxy of any type for getting data from OpenWeatherMap.org + * + * @param host Host address of the proxy + * @param port Port address of the proxy + * @param type Type of the proxy + */ + fun setProxy(host: String, port: Int, type: Proxy.Type): OWM { + proxy = Proxy(type, InetSocketAddress(host, port)) + setProxy(proxy) + + return this + } + + /** + * Set authenticated HTTP proxy for getting data from OpenWeatherMap.org + * + * @param host Host address of the proxy + * @param port Port address of the proxy + * @param user User name for the proxy if required + * @param pass Password for the proxy if required + */ + fun setProxy(host: String, port: Int, user: String, pass: String): OWM { + setProxy(host, port, user, pass, Proxy.Type.HTTP) + + return this + } + + /** + * Set authenticated proxy of any type for getting data from OpenWeatherMap.org + * + * @param host Host address of the proxy + * @param port Port address of the proxy + * @param user User name for the proxy if required + * @param pass Password for the proxy if required + * @param type Type of the proxy + */ + fun setProxy(host: String, port: Int, user: String, pass: String, type: Proxy.Type): OWM { + setProxy(host, port, type) + SystemTools.setProxyAuthDetails(user, pass) + + return this + } + + /** + * Remove proxy (i.e., direct connection) for getting data from OpenWeatherMap.org + */ + fun setNoProxy(): OWM { + setProxy(Proxy.NO_PROXY) + + return this + } + + /** + * Reset proxy to system's proxy for getting data from OpenWeatherMap.org + */ + fun resetProxy() { + setProxy(SystemTools.getSystemProxy()) + SystemTools.setProxyAuthDetails("", "") + } + + @Throws(APIException::class) + fun currentWeatherByCityName(cityName: String): CurrentWeather { + val api = retrofit.create(CurrentWeatherAPI::class.java) + + val apiCall = api.getCurrentWeatherByCityName(cityName) + val apiResp = apiCall.execute() + var weather = apiResp.body() + + if (weather == null) { + // TODO: if got weather, then check its respCode and throw if it is an error + // Also, setup a conf. for toggling this - if someone wish to handle himself + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + weather = CurrentWeather() + } + + return weather + } + + @Throws(APIException::class) + fun currentWeatherByCityName(cityName: String, countryCode: OWM.Country): CurrentWeather { + return currentWeatherByCityName(cityName + "," + countryCode) + } + + @Throws(APIException::class) + fun currentWeatherByCityId(cityId: Int): CurrentWeather { + val api = retrofit.create(CurrentWeatherAPI::class.java) + + val apiCall = api.getCurrentWeatherByCityId(cityId) + val apiResp = apiCall.execute() + var weather = apiResp.body() + + if (weather == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + weather = CurrentWeather() + } + + return weather + } + + @Throws(APIException::class) + fun currentWeatherByCoords(latitude: Float, longitude: Float): CurrentWeather { + val api = retrofit.create(CurrentWeatherAPI::class.java) + + val apiCall = api.getCurrentWeatherByCoords(latitude, longitude) + val apiResp = apiCall.execute() + var weather = apiResp.body() + + if (weather == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + weather = CurrentWeather() + } + + return weather + } + + @Throws(APIException::class) + fun currentWeatherByZipCode(zipCode: Int): CurrentWeather { + return currentWeatherByZipCode(zipCode, OWM.Country.UNITED_STATES) + } + + @Throws(APIException::class) + fun currentWeatherByZipCode(zipCode: Int, countryCode: OWM.Country): CurrentWeather { + val api = retrofit.create(CurrentWeatherAPI::class.java) + + val apiCall = api.getCurrentWeatherByZipCode(zipCode.toString() + "," + countryCode) + val apiResp = apiCall.execute() + var weather = apiResp.body() + + if (weather == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + weather = CurrentWeather() + } + + return weather + } + + @Throws(APIException::class) + fun hourlyForecastByCityName(cityName: String): HourlyForecast { + val api = retrofit.create(HourlyForecastAPI::class.java) + + val apiCall = api.getHourlyForecastByCityName(cityName) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = HourlyForecast() + } + + return forecast + } + + @Throws(APIException::class) + fun hourlyForecastByCityName(cityName: String, countryCode: OWM.Country): HourlyForecast { + return hourlyForecastByCityName(cityName + "," + countryCode) + } + + @Throws(APIException::class) + fun hourlyForecastByCityId(cityId: Int): HourlyForecast { + val api = retrofit.create(HourlyForecastAPI::class.java) + + val apiCall = api.getHourlyForecastByCityId(cityId) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = HourlyForecast() + } + + return forecast + } + + @Throws(APIException::class) + fun hourlyForecastByCoords(latitude: Float, longitude: Float): HourlyForecast { + val api = retrofit.create(HourlyForecastAPI::class.java) + + val apiCall = api.getHourlyForecastByCoords(latitude, longitude) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = HourlyForecast() + } + + return forecast + } + + @Throws(APIException::class) + fun hourlyForecastByZipCode(zipCode: Int): HourlyForecast { + return hourlyForecastByZipCode(zipCode, OWM.Country.UNITED_STATES) + } + + @Throws(APIException::class) + fun hourlyForecastByZipCode(zipCode: Int, countryCode: OWM.Country): HourlyForecast { + val api = retrofit.create(HourlyForecastAPI::class.java) + + val apiCall = api.getHourlyForecastByZipCode(zipCode.toString() + "," + countryCode) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = HourlyForecast() + } + + return forecast + } + + @Throws(APIException::class) + fun dailyForecastByCityName(cityName: String): DailyForecast { + return dailyForecastByCityName(cityName, OWM_25_DAILY_FORECAST_MAX_COUNT) + } + + @Throws(APIException::class) + fun dailyForecastByCityName(cityName: String, count: Byte): DailyForecast { + val api = retrofit.create(DailyForecastAPI::class.java) + + val apiCall = api.getDailyForecastByCityName(cityName, count) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = DailyForecast() + } + + return forecast + } + + @Throws(APIException::class) + fun dailyForecastByCityName(cityName: String, countryCode: OWM.Country): DailyForecast { + return dailyForecastByCityName(cityName, countryCode, OWM_25_DAILY_FORECAST_MAX_COUNT) + } + + @Throws(APIException::class) + fun dailyForecastByCityName(cityName: String, countryCode: OWM.Country, count: Byte): DailyForecast { + return dailyForecastByCityName(cityName + "," + countryCode, count) + } + + @Throws(APIException::class) + fun dailyForecastByCityId(cityId: Int): DailyForecast { + return dailyForecastByCityId(cityId, OWM_25_DAILY_FORECAST_MAX_COUNT) + } + + @Throws(APIException::class) + fun dailyForecastByCityId(cityId: Int, count: Byte): DailyForecast { + val api = retrofit.create(DailyForecastAPI::class.java) + + val apiCall = api.getDailyForecastByCityId(cityId, count) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = DailyForecast() + } + + return forecast + } + + @Throws(APIException::class) + fun dailyForecastByCoords(latitude: Float, longitude: Float): DailyForecast { + return dailyForecastByCoords(latitude, longitude, OWM_25_DAILY_FORECAST_MAX_COUNT) + } + + @Throws(APIException::class) + fun dailyForecastByCoords(latitude: Float, longitude: Float, count: Byte): DailyForecast { + val api = retrofit.create(DailyForecastAPI::class.java) + + val apiCall = api.getDailyForecastByCoords(latitude, longitude, count) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = DailyForecast() + } + + return forecast + } + + @Throws(APIException::class) + fun dailyForecastByZipCode(zipCode: Int): DailyForecast { + return dailyForecastByZipCode(zipCode, OWM.Country.UNITED_STATES, OWM_25_DAILY_FORECAST_MAX_COUNT) + } + + @Throws(APIException::class) + fun dailyForecastByZipCode(zipCode: Int, count: Byte): DailyForecast { + return dailyForecastByZipCode(zipCode, OWM.Country.UNITED_STATES, count) + } + + @Throws(APIException::class) + fun dailyForecastByZipCode(zipCode: Int, countryCode: OWM.Country): DailyForecast { + return dailyForecastByZipCode(zipCode, countryCode, OWM_25_DAILY_FORECAST_MAX_COUNT) + } + + @Throws(APIException::class) + fun dailyForecastByZipCode(zipCode: Int, countryCode: OWM.Country, count: Byte): DailyForecast { + val api = retrofit.create(DailyForecastAPI::class.java) + + val apiCall = api.getDailyForecastByZipCode(zipCode.toString() + "," + countryCode, count) + val apiResp = apiCall.execute() + var forecast = apiResp.body() + + if (forecast == null) { + if (!apiResp.isSuccessful) { + throw APIException(apiResp.code(), apiResp.message()) + } + + forecast = DailyForecast() + } + + return forecast + } + + /** + * Init Retrofit for getting data from OpenWeatherMap.org + * + * @param proxy Proxy + */ + private fun createRetrofitInstance(proxy: Proxy): Retrofit { + val clientBuilder = OkHttpClient.Builder().proxy(proxy) + + OkHttpTools.addQueryParameter(clientBuilder, "appid", apiKey) + OkHttpTools.addQueryParameter(clientBuilder, "type", accuracy.toString()) + OkHttpTools.addQueryParameter(clientBuilder, "lang", lang.toString()) + + if (unit != OWM.Unit.STANDARD) { + OkHttpTools.addQueryParameter(clientBuilder, "units", unit.toString()) + } + + val client = clientBuilder.build() + val gson = GsonBuilder().setLenient().create() + + val builder = Retrofit.Builder() + .client(client) + .baseUrl(OWM_25_BASE_URL) + .addConverterFactory(GsonConverterFactory.create(gson)) + + return builder.build() + } + + /** + * Search accuracy that can be set for getting data from OpenWeatherMap.org + * + * [OpenWeatherMap.org's Search accuracy][https://openweathermap.org/current#accuracy] + */ + enum class Accuracy + constructor(private val accuracy: String) { + ACCURATE("accurate"), + LIKE("like") + } + + /** + * Unit that can be set for getting data from OpenWeatherMap.org + * + * [OpenWeatherMap.org's Units format][https://openweathermap.org/current#data] + */ + enum class Unit + constructor(private val unit: String) { + IMPERIAL("imperial"), + METRIC("metric"), + STANDARD("standard") + } + + /** + * Language that can be set for getting data from OpenWeatherMap.org + * + * [OpenWeatherMap.org's Multilingual support][https://openweathermap.org/current#multi] + */ + enum class Language + constructor(private val lang: String) { + ARABIC("ar"), + BULGARIAN("bg"), + CATALAN("ca"), + CHINESE_SIMPLIFIED("zh_cn"), + CHINESE_TRADITIONAL("zh_tw"), + CROATIAN("hr"), + CZECH("cz"), + DUTCH("nl"), + ENGLISH("en"), + FINNISH("fi"), + FRENCH("fr"), + GALICIAN("gl"), + GREEK("el"), + GERMAN("de"), + HUNGARIAN("hu"), + ITALIAN("it"), + JAPANESE("ja"), + KOREAN("kr"), + LATVIAN("la"), + LITHUANIAN("lt"), + MACEDONIAN("mk"), + PERSIAN("fa"), + POLISH("pl"), + PORTUGUESE("pt"), + ROMANIAN("ro"), + RUSSIAN("ru"), + SLOVAK("sk"), + SLOVENIAN("sl"), + SPANISH("es"), + SWEDISH("se"), + TURKISH("tr"), + UKRAINIAN("ua"), + VIETNAMESE("vi") + } + + /** + * Country that can be set for getting data from OpenWeatherMap.org + */ + enum class Country + constructor(private val country: String) { + AFGHANISTAN("AF"), + ALAND_ISLANDS("AX"), + ALBANIA("AL"), + ALGERIA("DZ"), + AMERICAN_SAMOA("AS"), + ANDORRA("AD"), + ANGOLA("AO"), + ANGUILLA("AI"), + ANTARCTICA("AQ"), + ANTIGUA_AND_BARBUDA("AG"), + ARGENTINA("AR"), + ARMENIA("AM"), + ARUBA("AW"), + AUSTRALIA("AU"), + AUSTRIA("AT"), + AZERBAIJAN("AZ"), + BAHAMAS("BS"), + BAHRAIN("BH"), + BANGLADESH("BD"), + BARBADOS("BB"), + BELARUS("BY"), + BELGIUM("BE"), + BELIZE("BZ"), + BENIN("BJ"), + BERMUDA("BM"), + BHUTAN("BT"), + BOLIVIA("BO"), + BOSNIA_AND_HERZEGOVINA("BA"), + BOTSWANA("BW"), + BOUVET_ISLAND("BV"), + BRAZIL("BR"), + BRITISH_INDIAN_OCEAN_TERRITORY("IO"), + BRITISH_VIRGIN_ISLANDS("VG"), + BRUNEI("BN"), + BULGARIA("BG"), + BURKINA_FASO("BF"), + BURUNDI("BI"), + CAMBODIA("KH"), + CAMEROON("CM"), + CANADA("CA"), + CAPE_VERDE("CV"), + CARIBBEAN_NETHERLANDS("BQ"), + CAYMAN_ISLANDS("KY"), + CENTRAL_AFRICAN_REPUBLIC("CF"), + CHAD("TD"), + CHILE("CL"), + CHINA("CN"), + CHRISTMAS_ISLAND("CX"), + COCOS_KEELING_ISLANDS("CC"), + COLOMBIA("CO"), + COMOROS("KM"), + CONGO_BRAZZAVILLE("CG"), + CONGO_KINSHASA("CD"), + COOK_ISLANDS("CK"), + COSTA_RICA("CR"), + CROATIA("HR"), + CUBA("CU"), + CURACAO("CW"), + CYPRUS("CY"), + CZECH_REPUBLIC("CZ"), + COTE_D_IVOIRE("CI"), + DENMARK("DK"), + DJIBOUTI("DJ"), + DOMINICA("DM"), + DOMINICAN_REPUBLIC("DO"), + ECUADOR("EC"), + EGYPT("EG"), + EL_SALVADOR("SV"), + EQUATORIAL_GUINEA("GQ"), + ERITREA("ER"), + ESTONIA("EE"), + ETHIOPIA("ET"), + FALKLAND_ISLANDS("FK"), + FAROE_ISLANDS("FO"), + FIJI("FJ"), + FINLAND("FI"), + FRANCE("FR"), + FRENCH_GUIANA("GF"), + FRENCH_POLYNESIA("PF"), + FRENCH_SOUTHERN_TERRITORIES("TF"), + GABON("GA"), + GAMBIA("GM"), + GEORGIA("GE"), + GERMANY("DE"), + GHANA("GH"), + GIBRALTAR("GI"), + GREECE("GR"), + GREENLAND("GL"), + GRENADA("GD"), + GUADELOUPE("GP"), + GUAM("GU"), + GUATEMALA("GT"), + GUERNSEY("GG"), + GUINEA("GN"), + GUINEA_BISSAU("GW"), + GUYANA("GY"), + HAITI("HT"), + HEARD_AND_MCDONALD_ISLANDS("HM"), + HONDURAS("HN"), + HONG_KONG_SAR_CHINA("HK"), + HUNGARY("HU"), + ICELAND("IS"), + INDIA("IN"), + INDONESIA("ID"), + IRAN("IR"), + IRAQ("IQ"), + IRELAND("IE"), + ISLE_OF_MAN("IM"), + ISRAEL("IL"), + ITALY("IT"), + JAMAICA("JM"), + JAPAN("JP"), + JERSEY("JE"), + JORDAN("JO"), + KAZAKHSTAN("KZ"), + KENYA("KE"), + KIRIBATI("KI"), + KUWAIT("KW"), + KYRGYZSTAN("KG"), + LAOS("LA"), + LATVIA("LV"), + LEBANON("LB"), + LESOTHO("LS"), + LIBERIA("LR"), + LIBYA("LY"), + LIECHTENSTEIN("LI"), + LITHUANIA("LT"), + LUXEMBOURG("LU"), + MACAU_SAR_CHINA("MO"), + MACEDONIA("MK"), + MADAGASCAR("MG"), + MALAWI("MW"), + MALAYSIA("MY"), + MALDIVES("MV"), + MALI("ML"), + MALTA("MT"), + MARSHALL_ISLANDS("MH"), + MARTINIQUE("MQ"), + MAURITANIA("MR"), + MAURITIUS("MU"), + MAYOTTE("YT"), + MEXICO("MX"), + MICRONESIA("FM"), + MOLDOVA("MD"), + MONACO("MC"), + MONGOLIA("MN"), + MONTENEGRO("ME"), + MONTSERRAT("MS"), + MOROCCO("MA"), + MOZAMBIQUE("MZ"), + MYANMAR_BURMA("MM"), + NAMIBIA("NA"), + NAURU("NR"), + NEPAL("NP"), + NETHERLANDS("NL"), + NEW_CALEDONIA("NC"), + NEW_ZEALAND("NZ"), + NICARAGUA("NI"), + NIGER("NE"), + NIGERIA("NG"), + NIUE("NU"), + NORFOLK_ISLAND("NF"), + NORTH_KOREA("KP"), + NORTHERN_MARIANA_ISLANDS("MP"), + NORWAY("NO"), + OMAN("OM"), + PAKISTAN("PK"), + PALAU("PW"), + PALESTINIAN_TERRITORIES("PS"), + PANAMA("PA"), + PAPUA_NEW_GUINEA("PG"), + PARAGUAY("PY"), + PERU("PE"), + PHILIPPINES("PH"), + PITCAIRN_ISLANDS("PN"), + POLAND("PL"), + PORTUGAL("PT"), + PUERTO_RICO("PR"), + QATAR("QA"), + ROMANIA("RO"), + RUSSIA("RU"), + RWANDA("RW"), + REUNION("RE"), + SAMOA("WS"), + SAN_MARINO("SM"), + SAUDI_ARABIA("SA"), + SENEGAL("SN"), + SERBIA("RS"), + SEYCHELLES("SC"), + SIERRA_LEONE("SL"), + SINGAPORE("SG"), + SINT_MAARTEN("SX"), + SLOVAKIA("SK"), + SLOVENIA("SI"), + SOLOMON_ISLANDS("SB"), + SOMALIA("SO"), + SOUTH_AFRICA("ZA"), + SOUTH_GEORGIA_AND_SOUTH_SANDWICH_ISLANDS("GS"), + SOUTH_KOREA("KR"), + SOUTH_SUDAN("SS"), + SPAIN("ES"), + SRI_LANKA("LK"), + ST_BARTHELEMY("BL"), + ST_HELENA("SH"), + ST_KITTS_AND_NEVIS("KN"), + ST_LUCIA("LC"), + ST_MARTIN("MF"), + ST_PIERRE_AND_MIQUELON("PM"), + ST_VINCENT_AND_GRENADINES("VC"), + SUDAN("SD"), + SURINAME("SR"), + SVALBARD_AND_JAN_MAYEN("SJ"), + SWAZILAND("SZ"), + SWEDEN("SE"), + SWITZERLAND("CH"), + SYRIA("SY"), + SAO_TOME_AND_PRINCIPE("ST"), + TAIWAN("TW"), + TAJIKISTAN("TJ"), + TANZANIA("TZ"), + THAILAND("TH"), + TIMOR_LESTE("TL"), + TOGO("TG"), + TOKELAU("TK"), + TONGA("TO"), + TRINIDAD_AND_TOBAGO("TT"), + TUNISIA("TN"), + TURKEY("TR"), + TURKMENISTAN("TM"), + TURKS_AND_CAICOS_ISLANDS("TC"), + TUVALU("TV"), + US_OUTLYING_ISLANDS("UM"), + US_VIRGIN_ISLANDS("VI"), + UGANDA("UG"), + UKRAINE("UA"), + UNITED_ARAB_EMIRATES("AE"), + UNITED_KINGDOM("GB"), + UNITED_STATES("US"), + URUGUAY("UY"), + UZBEKISTAN("UZ"), + VANUATU("VU"), + VATICAN_CITY("VA"), + VENEZUELA("VE"), + VIETNAM("VN"), + WALLIS_AND_FUTUNA("WF"), + WESTERN_SAHARA("EH"), + YEMEN("YE"), + ZAMBIA("ZM"), + ZIMBABWE("ZW") + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeather.kt b/src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeather.kt new file mode 100644 index 0000000..0152544 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeather.kt @@ -0,0 +1,118 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName +import net.aksingh.owmjapis.model.param.* +import java.util.* + +data class CurrentWeather( + @field:SerializedName("dt") + private val dt: Int? = null, + + @field:SerializedName("rain") + val rainData: Rain? = null, + + @field:SerializedName("snow") + val snowData: Snow? = null, + + @field:SerializedName("coord") + val coordData: Coord? = null, + + @field:SerializedName("weather") + val weatherList: List? = null, + + @field:SerializedName("name") + val cityName: String? = null, + + @field:SerializedName("cod") + val respCode: Int? = null, + + @field:SerializedName("main") + val mainData: Main? = null, + + @field:SerializedName("clouds") + val cloudData: Cloud? = null, + + @field:SerializedName("id") + val cityId: Int? = null, + + @field:SerializedName("sys") + val systemData: System? = null, + + @field:SerializedName("base") + val baseStation: String? = null, + + @field:SerializedName("wind") + val windData: Wind? = null +) { + + var dateTime: Date? = null + get() { + if (dt != null) { + return Date(dt!!.toLong() * 1000L) + } + return null + } + + fun hasDateTime(): Boolean = dateTime != null + + fun hasRainData(): Boolean = rainData != null + + fun hasSnowData(): Boolean = snowData != null + + fun hasCoordData(): Boolean = coordData != null + + fun hasWeatherList(): Boolean = weatherList != null + + fun hasCityName(): Boolean = cityName != null + + fun hasRespCode(): Boolean = respCode != null + + fun hasMainData(): Boolean = mainData != null + + fun hasCloudData(): Boolean = cloudData != null + + fun hasCityId(): Boolean = cityId != null + + fun hassystemData(): Boolean = systemData != null + + fun hasBaseStation(): Boolean = baseStation != null + + fun hasWindData(): Boolean = windData != null + + companion object Static { + @JvmStatic + fun fromJson(json: String): CurrentWeather { + return GsonBuilder().create().fromJson(json, CurrentWeather::class.java) + } + + @JvmStatic + fun toJson(pojo: CurrentWeather): String { + return GsonBuilder().create().toJson(pojo) + } + + @JvmStatic + fun toJsonPretty(pojo: CurrentWeather): String { + return GsonBuilder().setPrettyPrinting().create().toJson(pojo) + } + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeatherList.kt b/src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeatherList.kt new file mode 100644 index 0000000..d80b4ef --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/CurrentWeatherList.kt @@ -0,0 +1,62 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class CurrentWeatherList( + @field:SerializedName("cod") + var respCode: String? = null, + + @field:SerializedName("calctime") + var calcTime: Float? = null, + + @field:SerializedName("cnt") + var dataCount: Short? = null, + + @field:SerializedName("list") + var dataList: List? = null +) { + fun hasRespCode(): Boolean = respCode != null + + fun hasCalcTime(): Boolean = calcTime != null + + fun hasDataCount(): Boolean = dataCount != null + + fun hasDataList(): Boolean = dataList != null + + companion object Static { + @JvmStatic + fun fromJson(json: String): CurrentWeatherList { + return GsonBuilder().create().fromJson(json, CurrentWeatherList::class.java) + } + + @JvmStatic + fun toJson(pojo: CurrentWeatherList): String { + return GsonBuilder().create().toJson(pojo) + } + + @JvmStatic + fun toJsonPretty(pojo: CurrentWeatherList): String { + return GsonBuilder().setPrettyPrinting().create().toJson(pojo) + } + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/DailyForecast.kt b/src/main/kotlin/net/aksingh/owmjapis/model/DailyForecast.kt new file mode 100644 index 0000000..53d00c6 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/DailyForecast.kt @@ -0,0 +1,70 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName +import net.aksingh.owmjapis.model.param.City +import net.aksingh.owmjapis.model.param.Data + +data class DailyForecast( + @field:SerializedName("cod") + var respCode: String? = null, + + @field:SerializedName("message") + var message: Float? = null, + + @field:SerializedName("city") + var cityData: City? = null, + + @field:SerializedName("cnt") + var dataCount: Int? = null, + + @field:SerializedName("list") + var dataList: List? = null +) { + + fun hasRespCode(): Boolean = respCode != null + + fun hasMessage(): Boolean = message != null + + fun hasCityData(): Boolean = cityData != null + + fun hasDataCount(): Boolean = dataCount != null + + fun hasDataList(): Boolean = dataList != null + + companion object Static { + @JvmStatic + fun fromJson(json: String): DailyForecast { + return GsonBuilder().create().fromJson(json, DailyForecast::class.java) + } + + @JvmStatic + fun toJson(pojo: DailyForecast): String { + return GsonBuilder().create().toJson(pojo) + } + + @JvmStatic + fun toJsonPretty(pojo: DailyForecast): String { + return GsonBuilder().setPrettyPrinting().create().toJson(pojo) + } + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/HourlyForecast.kt b/src/main/kotlin/net/aksingh/owmjapis/model/HourlyForecast.kt new file mode 100644 index 0000000..1ec1899 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/HourlyForecast.kt @@ -0,0 +1,89 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName +import net.aksingh.owmjapis.model.param.City +import net.aksingh.owmjapis.model.param.Data + +/** + *

+ * Parses hourly forecast 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: + * Others: null + *

+ * + * @author Ashutosh Kumar Singh + * @version 2017/11/08 + * + * @see [OpenWeatherMap.org's Weather Data API](https://openweathermap.org/forecast) + * @since 2.5.1.0 + */ +data class HourlyForecast( + @field:SerializedName("cod") + var respCode: String? = null, + + @field:SerializedName("message") + var message: Float? = null, + + @field:SerializedName("city") + var cityData: City? = null, + + @field:SerializedName("cnt") + var dataCount: Int? = null, + + @field:SerializedName("list") + var dataList: List? = null +) { + + fun hasRespCode(): Boolean = respCode != null + + fun hasMessage(): Boolean = message != null + + fun hasCityData(): Boolean = cityData != null + + fun hasDataCount(): Boolean = dataCount != null + + fun hasDataList(): Boolean = dataList != null + + companion object Static { + @JvmStatic + fun fromJson(json: String): HourlyForecast { + return GsonBuilder().create().fromJson(json, HourlyForecast::class.java) + } + + @JvmStatic + fun toJson(pojo: HourlyForecast): String { + return GsonBuilder().create().toJson(pojo) + } + + @JvmStatic + fun toJsonPretty(pojo: HourlyForecast): String { + return GsonBuilder().setPrettyPrinting().create().toJson(pojo) + } + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/City.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/City.kt new file mode 100644 index 0000000..44e48c5 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/City.kt @@ -0,0 +1,59 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class City( + @field:SerializedName("id") + val id: Int? = null, + + @field:SerializedName("name") + val name: String? = null, + + @field:SerializedName("coord") + val coordData: Coord? = null, + + @field:SerializedName("country") + val countryCode: String? = null, + + @field:SerializedName("population") + val population: Long? = null +) { + + fun hasId(): Boolean = id != null + + fun hasName(): Boolean = name != null + + fun hasCoordData(): Boolean = coordData != null + + fun hasCountryCode(): Boolean = countryCode != null + + fun hasPopulation(): Boolean = population != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Cloud.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Cloud.kt new file mode 100644 index 0000000..f60cef4 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Cloud.kt @@ -0,0 +1,39 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Cloud( + @field:SerializedName("all") + val cloudiness: Int? = null +) { + + fun hasCloudiness(): Boolean = cloudiness != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Coord.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Coord.kt new file mode 100644 index 0000000..77042b3 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Coord.kt @@ -0,0 +1,44 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Coord( + @field:SerializedName("lon") + val longitude: Float? = null, + + @field:SerializedName("lat") + val latitude: Float? = null +) { + + fun hasLongitude(): Boolean = longitude != null + + fun hasLatitude(): Boolean = latitude != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Data.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Data.kt new file mode 100644 index 0000000..c5a314b --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Data.kt @@ -0,0 +1,93 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName +import java.util.* + +data class Data( + @field:SerializedName("dt") + private val dt: Int? = null, + + @field:SerializedName("main") + val mainData: Main? = null, + + @field:SerializedName("temp") + val tempData: Temp? = null, + + @field:SerializedName("pressure") + val pressure: Float? = null, + + @field:SerializedName("humidity") + val humidity: Int? = null, + + @field:SerializedName("weather") + val weatherList: List? = null, + + @field:SerializedName("clouds") + val cloudData: Cloud? = null, + + @field:SerializedName("wind") + val windData: Wind? = null, + + @field:SerializedName("sys") + val systemData: System? = null, + + @field:SerializedName("dt_txt") + val dateTimeText: String? = null +) { + + var dateTime: Date? = null + get() { + if (dt != null) { + return Date(dt.toLong() * 1000L) + } + return null + } + + fun hasDateTime(): Boolean = dateTime != null + + fun hasMainData(): Boolean = mainData != null + + fun hasTempData(): Boolean = tempData != null + + fun hasPressure(): Boolean = pressure != null + + fun hasHumidity(): Boolean = humidity != null + + fun hasWeatherList(): Boolean = weatherList != null + + fun hasCloudData(): Boolean = cloudData != null + + fun hasWindData(): Boolean = windData != null + + fun hassystemData(): Boolean = systemData != null + + fun hasDateTimeText(): Boolean = dateTimeText != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Main.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Main.kt new file mode 100644 index 0000000..62fd966 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Main.kt @@ -0,0 +1,74 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Main( + @field:SerializedName("temp") + val temp: Float? = null, + + @field:SerializedName("temp_min") + val tempMin: Float? = null, + + @field:SerializedName("temp_max") + val tempMax: Float? = null, + + @field:SerializedName("pressure") + val pressure: Float? = null, + + @field:SerializedName("sea_level") + val seaLevel: Float? = null, + + @field:SerializedName("grnd_level") + val groundLevel: Float? = null, + + @field:SerializedName("humidity") + val humidity: Int? = null, + + @field:SerializedName("temp_kf") + val tempKf: Float? = null +) { + + fun hasTemp(): Boolean = temp != null + + fun hasTempMax(): Boolean = tempMax != null + + fun hasTempMin(): Boolean = tempMin != null + + fun hasPressure(): Boolean = pressure != null + + fun hasSeaLevel(): Boolean = seaLevel != null + + fun hasGroundLevel(): Boolean = groundLevel != null + + fun hasHumidity(): Boolean = humidity != null + + fun hasTempKf(): Boolean = tempKf != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Rain.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Rain.kt new file mode 100644 index 0000000..cce1304 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Rain.kt @@ -0,0 +1,39 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Rain( + @field:SerializedName("3h") + val precipVol3h: Int? = null +) { + + fun hasPrecipVol3h(): Boolean = precipVol3h != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Snow.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Snow.kt new file mode 100644 index 0000000..c802476 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Snow.kt @@ -0,0 +1,39 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Snow( + @field:SerializedName("3h") + val snowVol3h: Int? = null +) { + + fun hasSnowVol3h(): Boolean = snowVol3h != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/System.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/System.kt new file mode 100644 index 0000000..0974a7a --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/System.kt @@ -0,0 +1,84 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName +import java.util.* + +data class System( + @field:SerializedName("type") + val type: Int? = null, + + @field:SerializedName("id") + val id: Int? = null, + + @field:SerializedName("message") + val message: Float? = null, + + @field:SerializedName("country") + val countryCode: String? = null, + + @field:SerializedName("sunrise") + private val sunrise: Int? = null, + + @field:SerializedName("sunset") + private val sunset: Int? = null, + + @field:SerializedName("pod") + val pod: String? = null +) { + + var sunriseDateTime: Date? = null + get() { + if (sunrise != null) { + return Date(sunrise.toLong() * 1000L) + } + return null + } + + var sunsetDateTime: Date? = null + get() { + if (sunset != null) { + return Date(sunset.toLong() * 1000L) + } + return null + } + + fun hasType(): Boolean = type != null + + fun hasId(): Boolean = id != null + + fun hasMessage(): Boolean = message != null + + fun hasCountryCode(): Boolean = countryCode != null + + fun hasSunriseDateTime(): Boolean = sunriseDateTime != null + + fun hasSunsetDateTime(): Boolean = sunsetDateTime != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Temp.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Temp.kt new file mode 100644 index 0000000..b16d894 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Temp.kt @@ -0,0 +1,64 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Temp( + @field:SerializedName("day") + val tempDay: Float? = null, + + @field:SerializedName("min") + val tempMin: Float? = null, + + @field:SerializedName("max") + val tempMax: Float? = null, + + @field:SerializedName("night") + val tempNight: Float? = null, + + @field:SerializedName("eve") + val tempEvening: Float? = null, + + @field:SerializedName("morn") + val tempMorning: Float? = null +) { + + fun hasTempDay(): Boolean = tempDay != null + + fun hasTempMin(): Boolean = tempMin != null + + fun hasTempMax(): Boolean = tempMax != null + + fun hasTempNight(): Boolean = tempNight != null + + fun hasTempEvening(): Boolean = tempEvening != null + + fun hasTempMorning(): Boolean = tempMorning != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Weather.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Weather.kt new file mode 100644 index 0000000..d5efdef --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Weather.kt @@ -0,0 +1,54 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Weather( + @field:SerializedName("id") + val conditionId: Int? = null, + + @field:SerializedName("main") + val mainInfo: String? = null, + + @field:SerializedName("description") + val moreInfo: String? = null, + + @field:SerializedName("icon") + val iconCode: String? = null +) { + + fun hasConditionId(): Boolean = conditionId != null + + fun hasMainInfo(): Boolean = mainInfo != null + + fun hasMoreInfo(): Boolean = moreInfo != null + + fun hasIconCode(): Boolean = iconCode != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/model/param/Wind.kt b/src/main/kotlin/net/aksingh/owmjapis/model/param/Wind.kt new file mode 100644 index 0000000..bc4c6d0 --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/model/param/Wind.kt @@ -0,0 +1,49 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.model.param + +import com.google.gson.GsonBuilder +import com.google.gson.annotations.SerializedName + +data class Wind( + @field:SerializedName("speed") + val speed: Float? = null, + + @field:SerializedName("deg") + val degree: Float? = null, + + @field:SerializedName("gust") + val gust: Float? = null +) { + + fun hasSpeed(): Boolean = speed != null + + fun hasDegree(): Boolean = degree != null + + fun hasGust(): Boolean = gust != null + + fun toJson(): String { + return GsonBuilder().create().toJson(this) + } + + fun toJsonPretty(): String { + return GsonBuilder().setPrettyPrinting().create().toJson(this) + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/util/ConversionTools.kt b/src/main/kotlin/net/aksingh/owmjapis/util/ConversionTools.kt new file mode 100644 index 0000000..7941b4a --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/util/ConversionTools.kt @@ -0,0 +1,110 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.util + +/** + * Provides methods for conversions, like converting + * windData direction from degree to direction code or name. + * + * @author Ashutosh Kumar Singh + * @version 2017-11-06 + * @since 2.5.1.0 + */ +class ConversionTools { + + companion object Static { + + /** + * Converts degree to direction code. + * + * @param degree Degree of windData (as received from OpenWeatherMap.org). + * @return Direction code, e.g., "N", "NE", etc. + * @throws IllegalArgumentException Degree should be between 0 and 360. + */ + @JvmStatic + @Throws(IllegalArgumentException::class) + fun convertDegree2DirectionCode(degree: Float): String { + val directionCode: String + + // degree should be between 0 and 360 + if (degree < 0.0f || degree > 360.0f) { + throw IllegalArgumentException("Degree cannot be less than 0 or more than 360.") + } + + when { + degree <= 11.25f -> directionCode = "N" + degree <= 33.75f -> directionCode = "NNE" + degree <= 56.25f -> directionCode = "NE" + degree <= 78.75f -> directionCode = "ENE" + degree <= 101.25f -> directionCode = "E" + degree <= 123.75f -> directionCode = "ESE" + degree <= 146.25f -> directionCode = "SE" + degree <= 168.75f -> directionCode = "SSE" + degree <= 191.25f -> directionCode = "S" + degree <= 213.75f -> directionCode = "SSW" + degree <= 236.25f -> directionCode = "SW" + degree <= 258.75f -> directionCode = "WSW" + degree <= 281.25f -> directionCode = "W" + degree <= 303.75f -> directionCode = "WNW" + degree <= 326.25f -> directionCode = "NW" + degree <= 348.75f -> directionCode = "NNW" + else -> directionCode = "N" + } + + return directionCode + } + + /** + * Converts degree to direction name. + * + * @param degree Degree of windData (as received from OpenWeatherMap.org). + * @return Direction name, e.g., "North", "North East", etc. + * @throws IllegalArgumentException Degree should be between 0 and 360. + */ + @JvmStatic + @Throws(IllegalArgumentException::class) + fun convertDegree2DirectionName(degree: Float): String { + val directionName: String + val directionCode = convertDegree2DirectionCode(degree) + + when (directionCode) { + "N" -> directionName = "North" + "NNE" -> directionName = "North North East" + "NE" -> directionName = "North East" + "ENE" -> directionName = "East North East" + "E" -> directionName = "East" + "ESE" -> directionName = "East South East" + "SE" -> directionName = "South East" + "SSE" -> directionName = "South South East" + "S" -> directionName = "South" + "SSW" -> directionName = "South South West" + "SW" -> directionName = "South West" + "WSW" -> directionName = "West South West" + "W" -> directionName = "West" + "WNW" -> directionName = "West North West" + "NW" -> directionName = "North West" + "NNW" -> directionName = "North North West" + else -> directionName = "North" + } + + return directionName + } + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/util/OkHttpTools.kt b/src/main/kotlin/net/aksingh/owmjapis/util/OkHttpTools.kt new file mode 100644 index 0000000..422608d --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/util/OkHttpTools.kt @@ -0,0 +1,62 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.util + +import okhttp3.OkHttpClient + +/** + * Provides methods for OkHttp lib. handling. + * + * @author Ashutosh Kumar Singh + * @version 2017-11-08 + * @since 2.5.1.0 + */ +internal class OkHttpTools { + + companion object Static { + + /** + * Add a query parameter to OkHttpClient.Builder instance. + * It is used to add default parameters to Retrofit instance. + * + * @param httpClientBuilder OkHttpClient.Builder instance + * @param key Key in the query string + * @param value Value in the query string + */ + @JvmStatic + fun addQueryParameter(httpClientBuilder: OkHttpClient.Builder, key: String, value: String) { + httpClientBuilder.addInterceptor { chain -> + val original = chain.request() + val originalHttpUrl = original.url() + + val url = originalHttpUrl.newBuilder() + .addQueryParameter(key, value) + .build() + + val requestBuilder = original.newBuilder() + .url(url) + + val request = requestBuilder.build() + + chain.proceed(request) + } + } + } +} diff --git a/src/main/kotlin/net/aksingh/owmjapis/util/SystemTools.kt b/src/main/kotlin/net/aksingh/owmjapis/util/SystemTools.kt new file mode 100644 index 0000000..dedc7fa --- /dev/null +++ b/src/main/kotlin/net/aksingh/owmjapis/util/SystemTools.kt @@ -0,0 +1,76 @@ +/************************************************************************************************** + * Copyright (c) 2013-2017 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.util + +import java.net.* + +/** + * Provides methods for system handling. + * + * @author Ashutosh Kumar Singh + * @version 2017-11-08 + * @since 2.5.1.0 + */ +internal class SystemTools { + + companion object Static { + + /** + * Get the system proxy configured + * + * @return Proxy default to the system + */ + @JvmStatic + fun getSystemProxy(): Proxy { + var sysProxy = Proxy.NO_PROXY + + var proxyList: List<*>? = null + try { + proxyList = ProxySelector.getDefault().select(URI("http://localhost/")) + } catch (e: URISyntaxException) { + e.printStackTrace() + } + + if (proxyList != null) { + val iter = proxyList.iterator() + while (iter.hasNext()) { + sysProxy = iter.next() as java.net.Proxy + } + } + + return sysProxy + } + + /** + * Set the proxy's authentication details + * + * @param user Username for the proxy + * @param pass Password for the proxy + */ + @JvmStatic + fun setProxyAuthDetails(user: String, pass: String) { + Authenticator.setDefault(object : Authenticator() { + public override fun getPasswordAuthentication(): PasswordAuthentication { + return PasswordAuthentication(user, pass.toCharArray()) + } + }) + } + } +} diff --git a/src/test/java/net/aksingh/owmjapis/CurrentWeatherTest.java b/src/test/java/net/aksingh/owmjapis/CurrentWeatherTest.java index b08ee11..ac97202 100644 --- a/src/test/java/net/aksingh/owmjapis/CurrentWeatherTest.java +++ b/src/test/java/net/aksingh/owmjapis/CurrentWeatherTest.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -35,32 +32,32 @@ import java.io.IOException; */ public class CurrentWeatherTest { - public static void main(String[] args) throws IOException { - OpenWeatherMap owm = new OpenWeatherMap(""); - CurrentWeather cw = owm.currentWeatherByCityName("London, UK"); + public static void main(String[] args) throws IOException { + OpenWeatherMap owm = new OpenWeatherMap(""); + CurrentWeather cw = owm.currentWeatherByCityName("London, UK"); - if (!cw.isValid()) { - System.out.println("Reponse is inValid!"); - } else { - System.out.println("Reponse is Valid!"); - System.out.println(); + if (!cw.isValid()) { + System.out.println("Reponse is inValid!"); + } else { + System.out.println("Reponse is Valid!"); + System.out.println(); - if (cw.hasBaseStation()) { - System.out.println("Base station: " + cw.getBaseStation()); - } - if (cw.hasDateTime()) { - System.out.println("Date time: " + cw.getDateTime()); - } - System.out.println(); + if (cw.hasBaseStation()) { + System.out.println("Base station: " + cw.getBaseStation()); + } + if (cw.hasDateTime()) { + System.out.println("Date time: " + cw.getDateTime()); + } + System.out.println(); - if (cw.hasCityCode()) { - System.out.println("City code: " + cw.getCityCode()); - } - if (cw.hasCityName()) { - System.out.println("City name: " + cw.getCityName()); - } - System.out.println(); - } + if (cw.hasCityCode()) { + System.out.println("City code: " + cw.getCityCode()); + } + if (cw.hasCityName()) { + System.out.println("City name: " + cw.getCityName()); + } + System.out.println(); } + } } diff --git a/src/test/java/net/aksingh/owmjapis/DailyForecastTest.java b/src/test/java/net/aksingh/owmjapis/DailyForecastTest.java index acacdb6..a9ec419 100644 --- a/src/test/java/net/aksingh/owmjapis/DailyForecastTest.java +++ b/src/test/java/net/aksingh/owmjapis/DailyForecastTest.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -35,43 +32,43 @@ import java.io.IOException; */ public class DailyForecastTest { - public static void main(String[] args) throws IOException { - OpenWeatherMap owm = new OpenWeatherMap(""); - DailyForecast df = owm.dailyForecastByCityName("London, UK", Byte.parseByte("5")); + public static void main(String[] args) throws IOException { + OpenWeatherMap owm = new OpenWeatherMap(""); + DailyForecast df = owm.dailyForecastByCityName("London, UK", Byte.parseByte("5")); - if (!df.isValid()) { - System.out.println("Reponse is inValid!"); - } else { - System.out.println("Reponse is Valid!"); - System.out.println(); + if (!df.isValid()) { + System.out.println("Reponse is inValid!"); + } else { + System.out.println("Reponse is Valid!"); + System.out.println(); - if (df.hasCityInstance()) { - DailyForecast.City city = df.getCityInstance(); - if (city.hasCityName()) { - if (city.hasCityCode()) { - System.out.println("City code: " + city.getCityCode()); - } - if (city.hasCityName()) { - System.out.println("City name: " + city.getCityName()); - } - System.out.println(); - } - } - - System.out.println("Total forecast instances: " + df.getForecastCount()); - System.out.println(); - - for (int i = 0; i < df.getForecastCount(); i++) { - DailyForecast.Forecast forecast = df.getForecastInstance(i); - - System.out.println("*** Forecast instance number " + (i+1) + " ***"); - - if (forecast.hasDateTime()) { - System.out.println(forecast.getDateTime()); - } - - System.out.println(); - } + if (df.hasCityInstance()) { + DailyForecast.City city = df.getCityInstance(); + if (city.hasCityName()) { + if (city.hasCityCode()) { + System.out.println("City code: " + city.getCityCode()); + } + if (city.hasCityName()) { + System.out.println("City name: " + city.getCityName()); + } + System.out.println(); } + } + + System.out.println("Total forecast instances: " + df.getForecastCount()); + System.out.println(); + + for (int i = 0; i < df.getForecastCount(); i++) { + DailyForecast.Forecast forecast = df.getForecastInstance(i); + + System.out.println("*** Data instance number " + (i + 1) + " ***"); + + if (forecast.hasDateTime()) { + System.out.println(forecast.getDateTime()); + } + + System.out.println(); + } } + } } diff --git a/src/test/java/net/aksingh/owmjapis/HourlyForecastTest.java b/src/test/java/net/aksingh/owmjapis/HourlyForecastTest.java index c9539ff..e682e70 100644 --- a/src/test/java/net/aksingh/owmjapis/HourlyForecastTest.java +++ b/src/test/java/net/aksingh/owmjapis/HourlyForecastTest.java @@ -1,24 +1,21 @@ -/* - * 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. - */ +/************************************************************************************************** + * Copyright (c) 2013-2017 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; @@ -35,43 +32,43 @@ import java.io.IOException; */ public class HourlyForecastTest { - public static void main(String[] args) throws IOException { - OpenWeatherMap owm = new OpenWeatherMap(""); - HourlyForecast hf = owm.hourlyForecastByCityName("London, UK"); + public static void main(String[] args) throws IOException { + OpenWeatherMap owm = new OpenWeatherMap(""); + HourlyForecast hf = owm.hourlyForecastByCityName("London, UK"); - if (!hf.isValid()) { - System.out.println("Reponse is inValid!"); - } else { - System.out.println("Reponse is Valid!"); - System.out.println(); + if (!hf.isValid()) { + System.out.println("Reponse is inValid!"); + } else { + System.out.println("Reponse is Valid!"); + System.out.println(); - if (hf.hasCityInstance()) { - HourlyForecast.City city = hf.getCityInstance(); - if (city.hasCityName()) { - if (city.hasCityCode()) { - System.out.println("City code: " + city.getCityCode()); - } - if (city.hasCityName()) { - System.out.println("City name: " + city.getCityName()); - } - System.out.println(); - } - } - - System.out.println("Total forecast instances: " + hf.getForecastCount()); - System.out.println(); - - for (int i = 0; i < hf.getForecastCount(); i++) { - HourlyForecast.Forecast forecast = hf.getForecastInstance(i); - - System.out.println("*** Forecast instance number " + (i+1) + " ***"); - - if (forecast.hasDateTime()) { - System.out.println(forecast.getDateTime()); - } - - System.out.println(); - } + if (hf.hasCityInstance()) { + HourlyForecast.City city = hf.getCityInstance(); + if (city.hasCityName()) { + if (city.hasCityCode()) { + System.out.println("City code: " + city.getCityCode()); + } + if (city.hasCityName()) { + System.out.println("City name: " + city.getCityName()); + } + System.out.println(); } + } + + System.out.println("Total forecast instances: " + hf.getForecastCount()); + System.out.println(); + + for (int i = 0; i < hf.getForecastCount(); i++) { + HourlyForecast.Forecast forecast = hf.getForecastInstance(i); + + System.out.println("*** Data instance number " + (i + 1) + " ***"); + + if (forecast.hasDateTime()) { + System.out.println(forecast.getDateTime()); + } + + System.out.println(); + } } + } }