Merge pull request 'develop' (#1) from develop into main

Reviewed-on: AdvancedProgramming1404/HW-03-oop-and-api#1
This commit is contained in:
2026-04-24 14:58:11 +00:00
12 changed files with 650 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.to</groupId>
<artifactId>HW03</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- GSON: Google JSON parsing library -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
<!-- Jackson: Another JSON parsing library -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
</project>
+108
View File
@@ -0,0 +1,108 @@
package Rational;
public class Rational {
private int numerator;
private int denominator;
// Constructor
public Rational(int numerator, int denominator) {
if (denominator == 0) {
throw new IllegalArgumentException("Denominator cannot be zero");
}
this.numerator = numerator;
this.denominator = denominator;
simplify();
}
// GCD helper for simplification
private int gcd(int a, int b) {
a = Math.abs(a);
b = Math.abs(b);
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
// Simplify the fraction
private void simplify() {
int gcd = gcd(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
// Keep denominator positive
if (denominator < 0) {
numerator = -numerator;
denominator = -denominator;
}
}
// COMPLETED - Instance method: this + other
public Rational add(Rational other) {
int newNumerator = this.numerator * other.denominator + other.numerator * this.denominator;
int newDenominator = this.denominator * other.denominator;
return new Rational(newNumerator, newDenominator);
}
// COMPLETED - Static method: r1 + r2
public static Rational add(Rational r1, Rational r2) {
return r1.add(r2);
}
// TODO: Instance method - this - other
// Formula: a/b - c/d = (a*d - c*b) / (b*d)
public Rational subtract(Rational other) {
// YOUR CODE HERE
return null; // Remove this line when implemented
}
// TODO: Static method - r1 - r2
public static Rational subtract(Rational r1, Rational r2) {
// YOUR CODE HERE
return null; // Remove this line when implemented
}
// TODO: Instance method - this * other
// Formula: (a/b) * (c/d) = (a*c) / (b*d)
public Rational multiply(Rational other) {
// YOUR CODE HERE
return null; // Remove this line when implemented
}
// TODO: Static method - r1 * r2
public static Rational multiply(Rational r1, Rational r2) {
// YOUR CODE HERE
return null; // Remove this line when implemented
}
// TODO: Instance method - this / other
// Formula: (a/b) ÷ (c/d) = (a*d) / (b*c)
public Rational divide(Rational other) {
// YOUR CODE HERE
// HINT: Division is multiplication by reciprocal
// Don't forget to check if other.numerator is zero!
return null; // Remove this line when implemented
}
// TODO: Static method - r1 / r2
public static Rational divide(Rational r1, Rational r2) {
// YOUR CODE HERE
return null; // Remove this line when implemented
}
// PROVIDED - String representation
@Override
public String toString() {
if (denominator == 1) {
return numerator + "";
}
return numerator + "/" + denominator;
}
// PROVIDED - Decimal value
public double toDouble() {
return (double) numerator / denominator;
}
}
+108
View File
@@ -0,0 +1,108 @@
package Rational;
public class RationalTest {
public static void main(String[] args) {
System.out.println("=== Testing Rational Operations ===\n");
// Create some rational numbers
Rational r1 = new Rational(1, 2); // 1/2
Rational r2 = new Rational(1, 3); // 1/3
Rational r3 = new Rational(3, 4); // 3/4
Rational r4 = new Rational(2, 5); // 2/5
Rational r5 = new Rational(5, 1); // 5 (integer)
System.out.println("Created rational numbers:");
System.out.println("r1 = " + r1);
System.out.println("r2 = " + r2);
System.out.println("r3 = " + r3);
System.out.println("r4 = " + r4);
System.out.println("r5 = " + r5);
System.out.println();
// Test ADDITION
System.out.println("=== ADDITION ===");
System.out.println("Instance method - r1 + r2 = " + r1.add(r2));
System.out.println("Static method - Rational.add(r1, r2) = " + Rational.add(r1, r2));
System.out.println("Expected: 1/2 + 1/3 = 5/6");
System.out.println("Decimal: " + r1.add(r2).toDouble());
System.out.println();
// Test SUBTRACTION
System.out.println("=== SUBTRACTION ===");
System.out.println("Instance method - r1 - r2 = " + r1.subtract(r2));
System.out.println("Static method - Rational.subtract(r1, r2) = " + Rational.subtract(r1, r2));
System.out.println("Expected: 1/2 - 1/3 = 1/6");
System.out.println("Decimal: " + r1.subtract(r2).toDouble());
System.out.println();
// Test MULTIPLICATION
System.out.println("=== MULTIPLICATION ===");
System.out.println("Instance method - r1 * r3 = " + r1.multiply(r3));
System.out.println("Static method - Rational.multiply(r1, r3) = " + Rational.multiply(r1, r3));
System.out.println("Expected: 1/2 * 3/4 = 3/8");
System.out.println("Decimal: " + r1.multiply(r3).toDouble());
System.out.println();
// Test DIVISION
System.out.println("=== DIVISION ===");
System.out.println("Instance method - r1 ÷ r2 = " + r1.divide(r2));
System.out.println("Static method - Rational.divide(r1, r2) = " + Rational.divide(r1, r2));
System.out.println("Expected: 1/2 ÷ 1/3 = 3/2");
System.out.println("Decimal: " + r1.divide(r2).toDouble());
System.out.println();
// Test with negative numbers
System.out.println("=== NEGATIVE NUMBERS ===");
Rational neg1 = new Rational(-2, 3);
Rational neg2 = new Rational(4, -5);
System.out.println("neg1 = " + neg1); // Should show -2/3
System.out.println("neg2 = " + neg2); // Should show -4/5
System.out.println("neg1 + neg2 = " + neg1.add(neg2));
System.out.println("-2/3 + -4/5 = (-10/15 + -12/15) = -22/15");
System.out.println("neg1 * neg2 = " + neg1.multiply(neg2));
System.out.println("(-2/3) * (-4/5) = 8/15");
System.out.println();
// Test simplification
System.out.println("=== SIMPLIFICATION ===");
Rational r6 = new Rational(6, 8);
Rational r7 = new Rational(10, 25);
Rational r8 = new Rational(18, -24);
System.out.println("6/8 simplified = " + r6);
System.out.println("10/25 simplified = " + r7);
System.out.println("18/-24 simplified = " + r8);
System.out.println();
// Test chaining operations
System.out.println("=== CHAINING OPERATIONS ===");
Rational result = r1.add(r2).multiply(r3).divide(r4);
System.out.println("((r1 + r2) * r3) ÷ r4 = " + result);
System.out.println("((1/2 + 1/3) * 3/4) ÷ 2/5 = (5/6 * 3/4) ÷ 2/5 = (15/24) ÷ 2/5 = 15/24 * 5/2 = 75/48 = 25/16");
System.out.println("Decimal: " + result.toDouble());
System.out.println();
// Test integer handling
System.out.println("=== INTEGER HANDLING ===");
System.out.println("r5 = " + r5); // Should show 5, not 5/1
System.out.println("r5 + r1 = " + r5.add(r1));
System.out.println("5 + 1/2 = 11/2");
System.out.println("r1 * r5 = " + r1.multiply(r5));
System.out.println("1/2 * 5 = 5/2");
System.out.println();
// Test exception handling
System.out.println("=== EXCEPTION TEST ===");
try {
Rational invalid = new Rational(1, 0);
} catch (IllegalArgumentException e) {
System.out.println("Caught exception: " + e.getMessage());
}
try {
Rational zero = new Rational(0, 1);
Rational divisionByZero = r1.divide(zero);
} catch (ArithmeticException e) {
System.out.println("Caught division by zero: " + e.getMessage());
}
}
}
+35
View File
@@ -0,0 +1,35 @@
package movie.apis;
public class Genre {
private Long id;
private String name;
public Genre(Long id, String name) {
this.id = id;
this.name = name;
}
public Genre() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return id + ". " + name;
}
}
+87
View File
@@ -0,0 +1,87 @@
package movie.apis;
import java.util.Scanner;
public class Main {
private static final MovieService movieService = new MovieService();
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("\n=== MOVIE API CLIENT ===\n");
boolean running = true;
while (running) {
displayMenu();
int choice = getIntInput("Enter your choice: ");
switch (choice) {
case 1:
movieService.displayGenres();
break;
case 2:
// TODO: Get genre ID and page from user, then call movieService.displayMoviesByGenre()
break;
case 3:
// TODO (BONUS): Get movie ID from user, then call movieService.displayMovieDetails()
break;
case 0:
running = false;
System.out.println("Goodbye!");
break;
default:
System.out.println("Invalid choice! Please enter 0-4");
}
if (running) {
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
scanner.nextLine();
}
}
scanner.close();
}
private static void displayMenu() {
System.out.println("\n================================");
System.out.println(" MENU");
System.out.println("================================");
System.out.println("1. Get all genres");
System.out.println("2. Get movies by genre ID");
System.out.println("3. Get movie details by ID");
System.out.println("0. Exit");
System.out.println("================================");
}
private static int getIntInput(String prompt) {
System.out.print(prompt);
while (!scanner.hasNextInt()) {
System.out.print("Invalid input. " + prompt);
scanner.next();
}
int input = scanner.nextInt();
scanner.nextLine();
return input;
}
private static Long getLongInput(String prompt) {
System.out.print(prompt);
while (!scanner.hasNextLong()) {
System.out.print("Invalid input. " + prompt);
scanner.next();
}
Long input = scanner.nextLong();
scanner.nextLine();
return input;
}
}
+24
View File
@@ -0,0 +1,24 @@
package movie.apis;
import java.util.List;
/**
* Model class representing a movie
* Fields: id, title, genres, images
*/
public class Movie {
private Long id;
private String title;
private List<Genre> genres;
private String images;
private String year;
private String imdbRating;
private String country;
// TODO: Create a constructor with all fields
// TODO: Create a simple constructor with id and title only
// TODO: Create getters and setters for all fields
}
+188
View File
@@ -0,0 +1,188 @@
package movie.apis;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class MovieService {
private final static String GENRES_URL = "https://api.meshcomp.ir/api/v1/genres";
private final static String MOVIE_BY_ID_URL = "https://api.meshcomp.ir/api/v1/movies/";
private final static HttpClient client = HttpClient.newHttpClient();
private String getGenresList() {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(GENRES_URL))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
System.out.println("Genres List received successfully");
return response.body();
} else {
throw new IOException("HTTP error code: " + response.statusCode());
}
} catch (Exception e) {
System.out.println("!!Exception : " + e.getMessage());
}
return null;
}
private String getMoviesByGenreList(Long genreId, Long page) {
try {
String url = "https://api.meshcomp.ir/api/v1/genres/" + genreId + "/movies?page=" + page;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return response.body();
} else {
System.out.println("HTTP error code: " + response.statusCode());
return null;
}
} catch (Exception e) {
System.out.println("!!Exception : " + e.getMessage());
}
return null;
}
public void displayGenres() {
String jsonResponse = getGenresList();
if (jsonResponse == null) {
System.out.println("Failed to get genres");
return;
}
try {
JsonObject rootObject = JsonParser.parseString(jsonResponse).getAsJsonObject();
JsonArray genresArray = rootObject.getAsJsonArray("genres");
Type genreListType = new TypeToken<ArrayList<Genre>>(){}.getType();
Gson gson = new Gson();
List<Genre> genres = gson.fromJson(genresArray, genreListType);
System.out.println("\nALL GENRES:");
for (Genre genre : genres) {
System.out.println(genre.getId() + ". " + genre.getName());
}
System.out.println();
} catch (Exception e) {
System.err.println("ERROR parsing genres: " + e.getMessage());
}
}
/**
* TODO 1: Display movies for a specific genre and page
*
* JSON format: { "movies": [ { "id": 1, "title": "Movie", "year": "1994" } ], "total": 177, "total_pages": 18 }
*
* Steps:
* - Call getMoviesByGenreList(genreId, page) to get JSON
* - Parse JSON to JsonObject
* - Get "movies" array (NOT "data")
* - Loop through array and print: "ID: {id} | {title} ({year})"
* - Also print total and total_pages
*
* PUT ALL PARSING CODE INSIDE try-catch
*/
public void displayMoviesByGenre(Long genreId, Long page) {
// TODO: Write your code here
// All parsing code must be inside try-catch
try {
// Your code here
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
}
}
/**
* BONUS: Fetch movie details from API using movie ID
* URL format: https://api.meshcomp.ir/api/v1/movies/{movieId}
*/
private String getMovieById(Long movieId) {
try {
// TODO: Complete this method
// String url = MOVIE_BY_ID_URL + movieId;
// HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
// HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// if (response.statusCode() == 200) return response.body();
return null;
} catch (Exception e) {
System.out.println("!!Exception : " + e.getMessage());
}
return null;
}
/**
* BONUS TODO: Display complete movie details for a given movie ID
*
* Expected JSON format from getMovieById():
* {
* "id": 1,
* "title": "The Shawshank Redemption",
* "year": "1994",
* "genres": ["Crime", "Drama"],
* "poster": "https://moviesapi.ir/images/tt0111161_poster.jpg",
* "country": "USA",
* "imdb_rating": "9.3"
* }
*
* INSTRUCTIONS:
* 1. Call getMovieById(movieId) to get the JSON response
* 2. Check if response is null (if yes, print error and return)
* 3. Parse the JSON string to JsonObject (inside try-catch)
* 4. Extract all fields: id, title, year, genres array, poster, country, imdb_rating
* 5. Convert genres array to comma-separated string
* 6. Print all movie details in a readable format
*
* PUT ALL PARSING CODE INSIDE THE EXISTING try-catch
*
* @param movieId The ID of the movie to display
*/
public void displayMovieDetails(Long movieId) {
// Step 1: Get JSON response from API
String jsonResponse = getMovieById(movieId);
// Step 2: Check if we got valid data
if (jsonResponse == null) {
System.out.println("Failed to get movie details for ID: " + movieId);
return;
}
// Step 3-6: Parse JSON and display details (PUT YOUR CODE INSIDE try-catch)
try {
// TODO: Parse jsonResponse to JsonObject
// TODO: Extract id, title, year, genres array, poster, country, imdb_rating
// TODO: Convert genres array to a readable string
// TODO: Print all movie details
// HINT: Use JsonParser.parseString(jsonResponse).getAsJsonObject()
// HINT: Use getAsJsonArray("genres") to get genres
// HINT: For each genre, use getAsString()
// Your code here:
} catch (Exception e) {
System.err.println("ERROR parsing movie details: " + e.getMessage());
}
}
}