Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • meissnerfl73755/softa
1 result
Show changes
Showing
with 346 additions and 0 deletions
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
\ No newline at end of file
MIT License
Copyright (c) 2017 HS Rosenheim :: Informatik :: Programmieren 3
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.
\ No newline at end of file
_This is an assignment to the [Software Architecture](https://ohm-softa.github.io) class at the [Technische Hochschule Nürnberg](http://www.th-nuernberg.de)._
# Assignment 6: Annotations and Reflection
[![Travis CI](https://travis-ci.org/hsro-inf-prg3/06-annotations-reflection.svg?branch=master)](https://travis-ci.org/hsro-inf-prg3/06-annotations-reflection)
In this assignment we will use Java annotations and reflection to interact with a remote REST ([Representational State Transfer](https://en.wikipedia.org/wiki/Representational_state_transfer)) API.
As everyone (or maybe just me) loves Chuck Norris jokes we will implement a simple program to get random Chuck Norris jokes from the [CNJDB](https://api.chucknorris.io/) (**C**huck **N**orris **J**okes **D**ata**b**ase).
## Setup
1. Create a fork of this repository (button in the right upper corner)
2. Clone the project (get the link by clicking the green _Clone or download button_)
3. Import the project to your IDE (remember the guide in [assignment 1](https://github.com/hsro-inf-prg3/01-tools))
4. Validate your environment by running the tests from your IntelliJ and by running `gradle test` on the command line.
## Gradle and Dependency Management
When we started to use Gradle we already talked about dependency management.
In this assignment we will use Gradle to manage the required libraries.
To complete this assignment you will need the following libraries:
* [Retrofit](http://square.github.io/retrofit/) by Square
* [Gson](https://github.com/google/gson) by Google
With Gradle, project dependencies (both at compile and runtime) are specified in the `build.gradle` file, in the `dependencies` section.
Open the existing [build.gradle](./build.gradle) file and inspect the `dependencies` object (Gradle uses [Groovy](http://groovy-lang.org/), a language similar to Java and Javascript).
Every dependency has a scope where it will be available.
To use a library across the whole project, declare it with the scope `implementation`.
Gradle is designed to help you in all development phases and is extensible by plugins.
In the given `build.gradle` are three plugins already applied:
* `java`: brings Java support to Gradle e.g. compilation)
* `application`: enable you to run and package the application you will develop in this assignment
* `idea`: helps with IntelliJ import
To run the `main` method in the `App` class without IntelliJ you can now use the following Gradle command on the command line:
```bash
gradle run
```
## Overview
The hard part of this assigment is you need to combine three parts to form the whole program:
- Gson for serialization
- Retrofit for HTTP requests
- A Gson type adapter to handle the status of the request response
It is strongly advised to read through the whole assignment and related documentations first; having the complete picture before starting with the parts helps a lot!
## Gson
Google Gson is a library to serialize and deserialize [JSON](https://en.wikipedia.org/wiki/JSON) to or from Java objects.
### Model
The following code snippet shows the structure of a simple JSON object:
```json
{
"id": "id-13434",
"value": "Ghosts are actually caused by Chuck Norris killing people faster than Death can process them.",
"categories": []
}
```
The most basic use case is to de/serialize objects; by defaut, Gson uses reflection to determine the properties.
```java
class Joke {
String id;
String value;
String[] categories;
}
```
```java
Gson gson = new Gson();
// JSON String --> Object
Joke j = gson.fromJson("{\"id\": 0, \"value\": \"Haha.\"}", Joke.class);
// categories remains `null`
// Objec --> JSON String
String json = gson.toJson(j);
```
Gson makes use of annotations to map JSON keys to fields of your class.
Have a look at the [docs](https://github.com/google/gson/blob/master/UserGuide.md) and complete the model described in the following UML:
![Model spec](./assets/images/ModelSpec.svg)
> Hint: the given JSON object describes the exact structure of the JSON objects we want to deserialize.
> Use anntations to help gson map JSON fields to differently named Java field names.
- Import Gson to your project
- Familiarize yourself with Gson by trying a few examples
- Get familiar with the `@SerializedName` annotation
## Retrofit and Gson
As you could see from the examples above, the actual response body of the CNJDB API looks like the following:
```json
{
"categories": ["nerdy"],
"id": "irKXY3NtTXGe6W529sVlOg",
"value": "Chuck Norris can delete the Recycling Bin."
}
```
The actual joke (`Joke`) is wrapped inside a response object which indicates if the request was successfull.
To be able to unwrap the jokes correctly (or throw an exception if there is no joke) you need to implement a Gson type adapter as shown in the following UML.
![Gson type adapter](./assets/images/GsonSpec.svg)
In a nutshell, a (Gson) type adapter is responsible to convert Java objects to JSON notation and vice versa.
Key to this transformation is in the implementation of the following two methods:
```java
public abstract class TypeAdapter<T> {
public abstract T read(final JsonReader reader);
public abstract void write(final JsonWriter writer, final T inst);
// ...
}
```
- Write a type adapter that accepts the response objects from CNJDB and outputs an instance of `Joke`.
- Register the type adapter with your Retrofit instance
Note that you can use annotations on the `Joke` class, but you will have to write custom code to unwrap the joke from the response object.
For this, you have two options:
* Implement a wrapper class, add appropriate fields, and return the `Joke` once unwrapped.
* Unwrap the response object manually, by using the `reader`'s `.beginObject()`, `.endObject()` and `.next*()` methods to determine the number of jokes.
> Note: There is no need to implement the `write` method, since we're only consuming the API, but not sending to it.
Check out this extensive [tutorial on Gson type adapters](https://github.com/albertattard/gson-typeadapter-example/).
## Retrofit
Retrofit is a great library to implement HTTP clients.
To create an HTTP client, create an interface containing some methods you will call later to perform HTTP requests.
Retrofit also uses annotations to conveniently map these methods to API resource paths, e.g. `getJokesBySearch("horse")` can be mapped to `GET https://api.chucknorris.io/jokes/search?query=horse`.
Read through the [Retrofit documentation](http://square.github.io/retrofit/) and implement the `CNJDBApi` interface as shown in the following UML:
![Retrofic spec](./assets/images/RetrofitAdapter.svg)
- Start by implementing the method `getRandomJoke()`; use the appropriate annotations to decodate the interface method.
- Modify the `main` method in the `App` class to create an instance of the `CNJDBApi` using `Retrofit.Builder`. You need to add a converter factory that helps converting the JSON response to an object; you can set Gson using `GsonConverterFactory.create()`.
- Print a random joke to `System.out`, and complete the test method `testCollision`. Recall that you work with `Call` objects that need to be executed before you can retrieve the response body.
- After completing the `getRandomJoke()` method try to add the other methods.
- If you are not sure if your query strings are correct you can test them within the command line using `curl` or in a browser extension such as [Postman](https://www.getpostman.com/).
Most unix systems will provide the cURL program:
```bash
curl -X GET "https://api.chucknorris.io/jokes/random" -H "accept: application/json"
```
On Windows, you can use the PowerShell to accomplish the same like so:
```ps
(Invoke-WebRequest
-Uri https://api.chucknorris.io/jokes/random
-Headers @{"accept"="application/json"}
).Content | ConvertFrom-Json | ConvertTo-Json
```
(The part `| ConvertFrom-Json | ConvertTo-Json` is only necessary for formatting.)
_Remark: to execute this command you have to remove the newlines!_
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="222px" preserveAspectRatio="none" style="width:192px;height:222px;" version="1.1" viewBox="0 0 192 222" width="192px" zoomAndPan="magnify"><defs><filter height="300%" id="f1gg8nmrixnqu4" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><!--cluster com.google.gson--><polygon fill="#FFFFFF" filter="url(#f1gg8nmrixnqu4)" points="14,16,140,16,147,41.0679,152,41.0679,152,102,14,102,14,16" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="14" x2="147" y1="41.0679" y2="41.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="120" x="18" y="32.9659">com.google.gson</text><!--cluster de.thro.inf.prg3.a06--><polygon fill="#FFFFFF" filter="url(#f1gg8nmrixnqu4)" points="17,124,160,124,167,149.0679,170,149.0679,170,210,17,210,17,124" style="stroke: #000000; stroke-width: 1.5;"/><line style="stroke: #000000; stroke-width: 1.5;" x1="17" x2="167" y1="149.0679" y2="149.0679"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="137" x="21" y="140.9659">de.thro.inf.prg3.a06</text><!--class TypeAdapter--><rect fill="#FEFECE" filter="url(#f1gg8nmrixnqu4)" height="48" id="TypeAdapter" style="stroke: #A80036; stroke-width: 1.5;" width="121" x="22.5" y="46"/><ellipse cx="37.5" cy="62" fill="#A9DCDF" rx="11" ry="11" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M40.4531,67.25 L39.5781,64.3594 L35.1563,64.3594 L34.2656,67.25 L31.5,67.25 L35.7813,55.0625 L38.9219,55.0625 L43.2344,67.25 L40.4531,67.25 Z M38.9688,62.2031 L38.0781,59.375 Q38,59.0938 37.8594,58.6484 Q37.7188,58.2031 37.5859,57.7422 Q37.4531,57.2813 37.3594,56.9531 Q37.2813,57.2813 37.1328,57.7891 Q36.9844,58.2969 36.8594,58.7422 Q36.7344,59.1875 36.6719,59.375 L35.7969,62.2031 L38.9688,62.2031 Z "/><ellipse cx="56.5" cy="61.5" fill="#84BE84" rx="3" ry="3" style="stroke: #038048; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="12" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="68" x="62.5" y="66.656">TypeAdapter</text><rect fill="#FFFFFF" height="18.3441" style="stroke: #000000; stroke-width: 1.0; stroke-dasharray: 2.0,2.0;" width="8" x="138.5" y="43"/><text fill="#000000" font-family="sans-serif" font-size="12" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="6" x="139.5" y="56.8281">T</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="23.5" x2="142.5" y1="78" y2="78"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="23.5" x2="142.5" y1="86" y2="86"/><!--class JokeAdapter--><rect fill="#FEFECE" filter="url(#f1gg8nmrixnqu4)" height="48" id="JokeAdapter" style="stroke: #A80036; stroke-width: 1.5;" width="111" x="27.5" y="154"/><ellipse cx="42.5" cy="170" fill="#ADD1B2" rx="11" ry="11" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M42.1875,166.3281 Q40.7031,166.3281 39.9219,167.4375 Q39.1406,168.5469 39.1406,170.4688 Q39.1406,172.4063 39.8594,173.4688 Q40.5781,174.5313 42.1875,174.5313 Q42.9219,174.5313 43.6641,174.3594 Q44.4063,174.1875 45.2813,173.875 L45.2813,176.0469 Q44.4688,176.3594 43.6875,176.5156 Q42.9063,176.6719 41.9375,176.6719 Q40.0781,176.6719 38.8594,175.8984 Q37.6406,175.125 37.0625,173.7188 Q36.4844,172.3125 36.4844,170.4531 Q36.4844,168.6094 37.1484,167.2031 Q37.8125,165.7969 39.0781,164.9922 Q40.3438,164.1875 42.1875,164.1875 Q43.0781,164.1875 43.9922,164.4219 Q44.9063,164.6563 45.7344,165.0469 L44.9063,167.1406 Q44.2188,166.8125 43.5313,166.5703 Q42.8438,166.3281 42.1875,166.3281 Z "/><ellipse cx="61.5" cy="169.5" fill="#84BE84" rx="3" ry="3" style="stroke: #038048; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="68" x="67.5" y="174.656">JokeAdapter</text><line style="stroke: #A80036; stroke-width: 1.5;" x1="28.5" x2="137.5" y1="186" y2="186"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="28.5" x2="137.5" y1="194" y2="194"/><!--link TypeAdapter to JokeAdapter--><path d="M83,114.2911 C83,127.8171 83,142.1835 83,153.8436 " fill="none" id="TypeAdapter-JokeAdapter" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="none" points="76.0001,114.2373,83,94.2373,90.0001,114.2373,76.0001,114.2373" style="stroke: #A80036; stroke-width: 1.0;"/><!--
@startuml GsonSpec
package com.google.gson {
+abstract class TypeAdapter<T> {
}
}
package de.thro.inf.prg3.a06 {
+class JokeAdapter extends TypeAdapter {
}
}
@enduml
PlantUML version 1.2018.12(Sun Oct 21 12:15:15 CEST 2018)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 1.8.0_192-b26
Operating System: Linux
OS Version: 4.18.16-arch1-1-ARCH
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="us-ascii" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="259px" preserveAspectRatio="none" style="width:331px;height:259px;background:#FFFFFF;" version="1.1" viewBox="0 0 331 259" width="331px" zoomAndPan="magnify"><defs/><g><!--cluster P1--><g id="cluster_P1"><path d="M9.5,6 L225.5,6 A5.25,5.25 0 0 1 229,9.5 L236,28.2969 L320.5,28.2969 A3.5,3.5 0 0 1 324,31.7969 L324,248.5 A3.5,3.5 0 0 1 320.5,252 L9.5,252 A3.5,3.5 0 0 1 6,248.5 L6,9.5 A3.5,3.5 0 0 1 9.5,6 " fill="none" style="stroke:#696969;stroke-width:1.0;"/><line style="stroke:#696969;stroke-width:1.0;" x1="6" x2="236" y1="28.2969" y2="28.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="217" x="10" y="20.9951">de.thro.inf.prg3.a06.model</text></g><!--class Joke--><g id="elem_Joke"><rect codeLine="3" fill="#F1F1F1" height="194.6719" id="Joke" rx="3.5" ry="3.5" style="stroke:#181818;stroke-width:0.5;" width="285" x="22.5" y="41"/><ellipse cx="139.75" cy="57" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1.0;"/><path d="M142.0938,52.6719 C141.1563,52.2344 140.5625,52.0938 139.6875,52.0938 C137.0625,52.0938 135.0625,54.1719 135.0625,56.8906 L135.0625,58.0156 C135.0625,60.5938 137.1719,62.4844 140.0625,62.4844 C141.2813,62.4844 142.4375,62.1875 143.1875,61.6406 C143.7656,61.2344 144.0938,60.7813 144.0938,60.3906 C144.0938,59.9375 143.7031,59.5469 143.2344,59.5469 C143.0156,59.5469 142.8125,59.625 142.625,59.8125 C142.1719,60.2969 142.1719,60.2969 141.9844,60.3906 C141.5625,60.6563 140.875,60.7813 140.1094,60.7813 C138.0625,60.7813 136.7656,59.6875 136.7656,57.9844 L136.7656,56.8906 C136.7656,55.1094 138.0156,53.7969 139.75,53.7969 C140.3281,53.7969 140.9375,53.9531 141.4063,54.2031 C141.8906,54.4844 142.0625,54.7031 142.1563,55.1094 C142.2188,55.5156 142.25,55.6406 142.3906,55.7656 C142.5313,55.9063 142.7656,56.0156 142.9844,56.0156 C143.25,56.0156 143.5156,55.875 143.6875,55.6563 C143.7969,55.5 143.8281,55.3125 143.8281,54.8906 L143.8281,53.4688 C143.8281,53.0313 143.8125,52.9063 143.7188,52.75 C143.5625,52.4844 143.2813,52.3438 142.9844,52.3438 C142.6875,52.3438 142.4844,52.4375 142.2656,52.75 L142.0938,52.6719 Z " fill="#000000"/><ellipse cx="165.25" cy="56.5" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="31" x="171.25" y="61.8467">Joke</text><line style="stroke:#181818;stroke-width:0.5;" x1="23.5" x2="306.5" y1="73" y2="73"/><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1.0;" width="6" x="30.5" y="83.6484"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="120" x="42.5" y="89.9951">identifier: String</text><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1.0;" width="6" x="30.5" y="99.9453"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="111" x="42.5" y="106.292">content: String</text><rect fill="none" height="6" style="stroke:#C82930;stroke-width:1.0;" width="6" x="30.5" y="116.2422"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="112" x="42.5" y="122.5889">rubrics: String[]</text><line style="stroke:#181818;stroke-width:0.5;" x1="23.5" x2="306.5" y1="129.8906" y2="129.8906"/><ellipse cx="33.5" cy="143.5391" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="155" x="42.5" y="146.8857">getIdentifier(): String</text><ellipse cx="33.5" cy="159.8359" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="259" x="42.5" y="163.1826">setIdentifier(identifier: String): void</text><ellipse cx="33.5" cy="176.1328" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="148" x="42.5" y="179.4795">getContent(): String</text><ellipse cx="33.5" cy="192.4297" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="243" x="42.5" y="195.7764">setContent(content: String): void</text><ellipse cx="33.5" cy="208.7266" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="151" x="42.5" y="212.0732">getRubrics(): String[]</text><ellipse cx="33.5" cy="225.0234" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="237" x="42.5" y="228.3701">setRubrics(rubrics: String[]): void</text></g><!--SRC=[TO-n3e8m48PtdkBSDCH6O-BWwgIJqT4ubFQ02z2Ijh8OtjqgGS3gZVVTVVVVpELO8JIK6AvSL7DHy0n18dEvqOnKocgJRHXVRLcb9PR0BPpMy8Z0LonARYqSTO6-yswzQaaIbQEKqEpWuWofR612A-Tf2CmzCIHi3wwtGHXdw0uVvtmHBCTsCX3UzokD9ZanxhiaVtGz_ShxItJkaWyYdtsiwddmuLZMHkqJ]--></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="us-ascii" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="177px" preserveAspectRatio="none" style="width:648px;height:177px;background:#FFFFFF;" version="1.1" viewBox="0 0 648 177" width="648px" zoomAndPan="magnify"><defs/><g><!--cluster P1--><g id="cluster_P1"><path d="M9.5,6 L127.5,6 A5.25,5.25 0 0 1 131,9.5 L138,28.2969 L637.5,28.2969 A3.5,3.5 0 0 1 641,31.7969 L641,166.5 A3.5,3.5 0 0 1 637.5,170 L9.5,170 A3.5,3.5 0 0 1 6,166.5 L6,9.5 A3.5,3.5 0 0 1 9.5,6 " fill="none" style="stroke:#696969;stroke-width:1.0;"/><line style="stroke:#696969;stroke-width:1.0;" x1="6" x2="138" y1="28.2969" y2="28.2969"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="119" x="10" y="20.9951">ohm.softa.a06</text></g><!--class CNJDBApi--><g id="elem_CNJDBApi"><rect codeLine="4" fill="#F1F1F1" height="113.1875" id="CNJDBApi" rx="3.5" ry="3.5" style="stroke:#181818;stroke-width:0.5;" width="364" x="261" y="41"/><ellipse cx="399.25" cy="57" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1.0;"/><path d="M400.2031,53.7813 L401.9219,53.7813 C402.3125,53.7813 402.5,53.75 402.625,53.6719 C402.8906,53.5156 403.0313,53.2344 403.0313,52.9375 C403.0313,52.6719 402.9219,52.4063 402.6875,52.2344 C402.5156,52.125 402.375,52.0938 401.9219,52.0938 L396.7813,52.0938 C396.3438,52.0938 396.2188,52.1094 396.0625,52.2031 C395.8125,52.3594 395.6563,52.6563 395.6563,52.9375 C395.6563,53.2188 395.7969,53.4688 396.0156,53.6406 C396.1719,53.75 396.3594,53.7813 396.7813,53.7813 L398.5,53.7813 L398.5,60.2969 L396.7813,60.2969 C396.3438,60.2969 396.2188,60.3125 396.0625,60.4219 C395.8125,60.5781 395.6563,60.8594 395.6563,61.1563 C395.6563,61.4063 395.7969,61.6719 396.0156,61.8281 C396.1719,61.9531 396.375,62 396.7813,62 L401.9219,62 C402.6719,62 403.0313,61.7188 403.0313,61.1563 C403.0313,60.875 402.9219,60.625 402.6875,60.4531 C402.5156,60.3281 402.375,60.2969 401.9219,60.2969 L400.2031,60.2969 L400.2031,53.7813 Z " fill="#000000"/><ellipse cx="424.75" cy="56.5" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="68" x="430.75" y="61.8467">CNJDBApi</text><line style="stroke:#181818;stroke-width:0.5;" x1="262" x2="624" y1="73" y2="73"/><line style="stroke:#181818;stroke-width:0.5;" x1="262" x2="624" y1="81" y2="81"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="213" x="267" y="97.9951">getRandomJoke(): Call&lt;Joke&gt;</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="352" x="267" y="114.292">getRandomJoke(categories: String[]): Call&lt;Joke&gt;</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="331" x="267" y="130.5889">getJokesBySearch(query: String): Call&lt;Joke[]&gt;</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="220" x="267" y="146.8857">getJoke(id: String): Call&lt;Joke&gt;</text></g><!--class App--><g id="elem_App"><rect codeLine="11" fill="#F1F1F1" height="64.2969" id="App" rx="3.5" ry="3.5" style="stroke:#181818;stroke-width:0.5;" width="203" x="22.5" y="65.5"/><ellipse cx="99.25" cy="81.5" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1.0;"/><path d="M101.5938,77.1719 C100.6563,76.7344 100.0625,76.5938 99.1875,76.5938 C96.5625,76.5938 94.5625,78.6719 94.5625,81.3906 L94.5625,82.5156 C94.5625,85.0938 96.6719,86.9844 99.5625,86.9844 C100.7813,86.9844 101.9375,86.6875 102.6875,86.1406 C103.2656,85.7344 103.5938,85.2813 103.5938,84.8906 C103.5938,84.4375 103.2031,84.0469 102.7344,84.0469 C102.5156,84.0469 102.3125,84.125 102.125,84.3125 C101.6719,84.7969 101.6719,84.7969 101.4844,84.8906 C101.0625,85.1563 100.375,85.2813 99.6094,85.2813 C97.5625,85.2813 96.2656,84.1875 96.2656,82.4844 L96.2656,81.3906 C96.2656,79.6094 97.5156,78.2969 99.25,78.2969 C99.8281,78.2969 100.4375,78.4531 100.9063,78.7031 C101.3906,78.9844 101.5625,79.2031 101.6563,79.6094 C101.7188,80.0156 101.75,80.1406 101.8906,80.2656 C102.0313,80.4063 102.2656,80.5156 102.4844,80.5156 C102.75,80.5156 103.0156,80.375 103.1875,80.1563 C103.2969,80 103.3281,79.8125 103.3281,79.3906 L103.3281,77.9688 C103.3281,77.5313 103.3125,77.4063 103.2188,77.25 C103.0625,76.9844 102.7813,76.8438 102.4844,76.8438 C102.1875,76.8438 101.9844,76.9375 101.7656,77.25 L101.5938,77.1719 Z " fill="#000000"/><ellipse cx="124.75" cy="81" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="30" x="130.75" y="86.3467">App</text><line style="stroke:#181818;stroke-width:0.5;" x1="23.5" x2="224.5" y1="97.5" y2="97.5"/><line style="stroke:#181818;stroke-width:0.5;" x1="23.5" x2="224.5" y1="105.5" y2="105.5"/><ellipse cx="33.5" cy="119.1484" fill="#84BE84" rx="3" ry="3" style="stroke:#038048;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" text-decoration="underline" textLength="177" x="42.5" y="122.4951">main(args: String[]): void</text></g><!--SRC=[TOyn2y9038Nt_eguLGJHnQ68e7Ng8697wH2lyHhQtjMxA1JfVrUnWgA-BSdxmdi3AQWckF31eP6WGLMY9h15FVRsA3Z6oGpGmtOAro20kqPiXDmH5K6yITQhPSFt4_JI93iqkQqJMr8uZ236gfe_XiKVZq8XRHsJZo0LnqRlix_SO-5NRKheL16UpkJQ5_NExBClAkJyb_Ffw_fggaBlOTaqRur6Loyeh3geaKs8Jdy6lLZE-oVT3G00]--></g></svg>
\ No newline at end of file
@startuml GsonSpec
package com.google.gson {
+abstract class TypeAdapter<T> {
}
}
package de.thro.inf.prg3.a06 {
+class JokeAdapter extends TypeAdapter {
}
}
@enduml
\ No newline at end of file
@startuml
!theme vibrant
package de.thro.inf.prg3.a06.model as P1 {
+class Joke {
-identifier: String
-content: String
-rubrics: String[]
+getIdentifier(): String
+setIdentifier(identifier: String): void
+getContent(): String
+setContent(content: String): void
+getRubrics(): String[]
+setRubrics(rubrics: String[]): void
}
}
@enduml
@startuml
!theme vibrant
package ohm.softa.a06 as P1 {
+interface CNJDBApi {
getRandomJoke(): Call<Joke>
getRandomJoke(categories: String[]): Call<Joke>
getJokesBySearch(query: String): Call<Joke[]>
getJoke(id: String): Call<Joke>
}
+class App {
+{static} main(args: String[]): void
}
}
@enduml
plugins {
id 'java'
id 'application'
id 'idea'
}
group 'ohm.softa'
version '1.0-SNAPSHOT'
sourceCompatibility = 11
mainClassName = "ohm.softa.a06.App"
repositories {
mavenCentral()
}
dependencies {
implementation("org.apache.commons:commons-lang3:$apacheCommonsLangVersion")
implementation("org.apache.logging.log4j:log4j-api:${log4j2Version}")
implementation("org.apache.logging.log4j:log4j-core:${log4j2Version}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-params:${junitVersion}")
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
File added
File added
File added
File added
File added
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="all">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
ohm/softa/a06/model/Joke.java
ohm.softa.a06.model.Joke
ohm/softa/a06/CNJDBApi.java
ohm.softa.a06.CNJDBApi
ohm/softa/a06/App.java
ohm.softa.a06.App
ohm.softa.a06.App$Test
ohm/softa/a06/tests/CNJDBTests.java
ohm.softa.a06.tests.CNJDBTests
junitVersion=5.7.1
log4j2Version=2.14.1
apacheCommonsLangVersion=3.12.0
squareRetrofitVersion=2.9.0
File added