Commit 460d8648 authored by Hallvard Trætteberg's avatar Hallvard Trætteberg
Browse files

lf-gren, master vil inneholde løsningen gjort om til oppgave

parent 6aac9376
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-12">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>kont2019</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
eclipse.preferences.version=1
encoding/<project>=UTF-8
encoding/src=UTF-8
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=12
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=12
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=12
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
<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>tdt4100</groupId>
<artifactId>risk</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>13.0.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<includes>
<include>**/*.fxml</include>
</includes>
</resource>
</resources>
<testSourceDirectory>src</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<encoding>UTF-8</encoding>
<release>12</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
</plugins>
</build>
</project>
package bike;
import java.util.ArrayList;
import java.util.List;
/*
* @startuml
* class Bike
* Bike --> Location: location
* Bike --> Person: renter
* @enduml
*/
public class Bike {
private GeoLocation location;
private Person renter;
public GeoLocation getLocation() {
return location;
}
void setLocation(final GeoLocation location) {
this.location = location;
}
public Person getRenter() {
return renter;
}
void setRenter(final Person renter) {
this.renter = renter;
}
// for computing rental price
private final List<RentalInfo> rentals = new ArrayList<RentalInfo>();
void addRentalInfo(final RentalInfo rentalInfo) {
rentals.add(rentalInfo);
}
List<RentalInfo> getRentalInfos() {
return new ArrayList<RentalInfo>(rentals);
}
void clearRentalInfos() {
rentals.clear();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="kont2019.bike.BikeRentalController">
<ImageView>
<image>
<Image url="@bikemap.png"/>
</image>
</ImageView>
<HBox>
<Label text="From: "/>
<TextField fx:id="fromInput" text="10:50" editable="false"/>
</HBox>
<HBox>
<Label text="To: "/>
<TextField fx:id="toInput" text="10:50"/>
<VBox>
<Button onAction="#plus1HourAction" text="+ 1 hour"/>
<Button onAction="#minus1HourAction" text="- 1 hour"/>
</VBox>
</HBox>
<Button onAction="#rentAction" text="Rent"/>
</VBox>
package bike;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
/*
* @startuml
* class Bike
*
* class Location {
* double latitude
* double longitude
* }
* Bike --> Location: location
*
* class BikeRental
* BikeRental *-- "*" Location: places
* BikeRental *-- "*" Bike: allBikes
*
* class Person
* class RentalInfo {
* LocalDateTime startTime
* LocalDateTime endTime
* }
* Bike --> Person: renter
* Bike *--> "*" RentalInfo: rentals
*
* interface PricePolicy
* class DefaultPricePolicy
* DefaultPricePolicy -up-|> PricePolicy
* BikeRental --> PricePolicy: pricePolicy
* Person -left-> PricePolicy: pricePolicy
*
* @enduml
*/
public class BikeRental {
private final Collection<GeoLocation> places = new ArrayList<GeoLocation>();
private final Collection<Bike> allBikes = new ArrayList<Bike>();
void addPlace(final GeoLocation location) {
places.add(location);
}
void addBike(final Bike bike, final GeoLocation location) {
if (location != null) {
bike.setLocation(location);
}
allBikes.add(bike);
}
/**
* Counts the number of available bikes within a certain distance of a provided location.
* @param location
* @param distance
* @return the number of available bikes within a certain distance of a provided location
*/
private int countAvailableBikesNearby(final GeoLocation location, final double distance) {
int count = 0;
for (final Bike bike : allBikes) {
if (bike.getRenter() == null && bike.getLocation().distance(location) <= distance) {
count++;
}
}
return count;
// return allBikes.stream().filter(bike -> bike.getRenter() == null && bike.getLocation().distance(location) <= distance).count();
}
/**
* Finds the closest station (location) within the provided (maximum) distance of the provided bike
* @param bike
* @param minDistance
* @return the closest station (location) within the provided (maximum) distance of the provided bike
*/
private GeoLocation getStationNearby(final Bike bike, final double maxDistance) {
GeoLocation closestPlace = null;
double minDistance = maxDistance;
for (final GeoLocation place : places) {
final double distance = bike.getLocation().distance(place);
if (distance < minDistance) {
closestPlace = place;
minDistance = distance;
}
}
return closestPlace;
}
/**
* @return the bikes that currently are rented
*/
private Collection<Bike> getRentedBikes() {
final Collection<Bike> result = new ArrayList<Bike>();
for (final Bike bike : allBikes) {
if (bike.getRenter() != null) {
result.add(bike);
}
}
return result;
// return allBikes.stream().filter(bike -> bike.getRenter() != null).collect(Collectors.toList());
}
/**
* @return the bikes that are close to a station (within 30m), but still are rented
*/
private Collection<Bike> getUnreturnedBikes() {
return getRentedBikes().stream().filter(bike -> getStationNearby(bike, 30.0) != null).collect(Collectors.toList());
}
/**
* Called when a person starts renting a bike by taking it from a station.
* Checks the arguments before registering all necessary info of the rental.
* @param person
* @param now the start time of the rental
* @param returnTime the expected return time
* @throws (some subclass of) RuntimeException if the now isn't before returnTime
* @throws (some subclass of) RuntimeException if the bike isn't available for rental
*/
public void rentBike(final Person person, final Bike bike, final LocalDateTime now, final LocalDateTime returnTime) {
checkNowIsBeforeReturnTime(now, returnTime);
if (bike.getRenter() != null) {
throw new IllegalArgumentException(bike + " is currently being rented");
}
bike.setRenter(person);
bike.addRentalInfo(new RentalInfo(now, returnTime));
}
private void checkNowIsBeforeReturnTime(final LocalDateTime now, final LocalDateTime returnTime) {
if (now.compareTo(returnTime) >= 0) {
throw new IllegalArgumentException("The start time, " + now + " is the same as or after the return time, " + returnTime);
}
}
/**
* Called when a person extends an ongoing bike rental.
* Checks the arguments before registering all necessary info of the rental extension.
* @param person
* @param bike
* @param now the time the extension starts
* @param returnTime the (new) expected return time
* @throws (some subclass of) RuntimeException if the now isn't before returnTime
* @throws (some subclass of) RuntimeException if the bike isn't currently being rented
* @throws (some subclass of) RuntimeException if person isn't currently renting the bike
*/
public void extendRental(final Person person, final Bike bike, final LocalDateTime now, final LocalDateTime returnTime) {
checkNowIsBeforeReturnTime(now, returnTime);
if (bike.getRenter() != person) {
throw new IllegalArgumentException(bike + " isn't currently being rented by " + person);
}
bike.addRentalInfo(new RentalInfo(now, returnTime));
}
/**
* Called when a person returns a bike.
* Checks the arguments, computes the price, performs the payment and clears the rental info.
* Note that if the payment isn't completed, the rental info should not be cleared.
* @param person
* @param bike
* @param now the time the bike is returned
* @throws (some subclass of) RuntimeException if the bike isn't currently being rented by the person argument
* @throws (some subclass of) RuntimeException if person isn't near (within 30m of) a station
*/
public void returnBike(final Person person, final Bike bike, final LocalDateTime now) {
if (bike.getRenter() != person) {
throw new IllegalArgumentException(bike + " isn't currently being rented by " + person);
}
final GeoLocation place = getStationNearby(bike, 30.0);
if (place == null) {
throw new IllegalArgumentException(bike + " isn't near (enough) a bike place");
}
person.withdraw(computePrice(person, bike, now));
bike.setRenter(null);
bike.clearRentalInfos();
}
private PricePolicy pricePolicy;
private int computePrice(final Person person, final Bike bike, final LocalDateTime returnTime) {
PricePolicy pricePolicy = person.getPricePolicy();
if (pricePolicy == null) {
pricePolicy = this.pricePolicy;
}
final int price = pricePolicy.computePrice(person, bike, returnTime);
return price;
}
}
package bike;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class BikeRentalApp extends Application {
@Override
public void start(final Stage primaryStage) throws Exception {
final Parent parent = FXMLLoader.load(getClass().getResource("BikeRental.fxml"));
primaryStage.setScene(new Scene(parent, 400, 525));
primaryStage.show();
}
public static void main(final String[] args) {
Application.launch(args);
}
}
package bike;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.util.converter.LocalTimeStringConverter;
public class BikeRentalController {
private final BikeRental bikeRental;
private final Person me;
private final Bike bike;
public BikeRentalController() {
bikeRental = new BikeRental();
final GeoLocation here = new GeoLocation(63, 10);
bikeRental.addPlace(here);
bike = new Bike();
bikeRental.addBike(bike, here);
me = new Person("Hallvard");
}
private final LocalTimeStringConverter localTimeStringConverter = new LocalTimeStringConverter();
@FXML
public void initialize() {
setFromTime(LocalTime.now());
setToTime(LocalTime.now().plus(1, ChronoUnit.HOURS));
Platform.runLater(() -> toInput.requestFocus());
}
@FXML
private TextField fromInput;
@FXML
private TextField toInput;
/**
* @return a LocalDataTime object corresponding to the from input field value
*/
private LocalTime getFromTime() {
return localTimeStringConverter.fromString(fromInput.getText());
}
/**
* Updates the from input field value according to the LocalDateTime argument
* @param time
*/
private void setFromTime(final LocalTime time) {
fromInput.setText(localTimeStringConverter.toString(time));
}
private LocalTime getToTime() {
return localTimeStringConverter.fromString(toInput.getText());
}
private void setToTime(final LocalTime time) {
toInput.setText(localTimeStringConverter.toString(time));
}
@FXML
public void plus1HourAction() {
setToTime(getToTime().plus(1, ChronoUnit.HOURS));
}
@FXML
public void minus1HourAction() {
setToTime(getToTime().minus(1, ChronoUnit.HOURS));
}
private LocalDateTime toLocalDateTime(final LocalTime time) {
return LocalDateTime.now().withHour(time.getHour()).withMinute(time.getMinute());
}
@FXML
public void rentAction() {
bikeRental.rentBike(me, bike, toLocalDateTime(getFromTime()) , toLocalDateTime(getToTime()));
}
}
package bike;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
public class DefaultPricePolicy implements PricePolicy {
protected int pricePrRental = 0, pricePrHour = 10, pricePrExtension = 5, pricePrLate = 10;
protected void setPricePrRental(final int pricePrRental) {
this.pricePrRental = pricePrRental;
}
protected void setPricePrHour(final int pricePrHour) {
this.pricePrHour = pricePrHour;
}
protected void setPricePrExtension(final int pricePrExtension) {
this.pricePrExtension = pricePrExtension;
}
protected void setPricePrLate(final int pricePrLate) {
this.pricePrLate = pricePrLate;
}
@Override
public int computePrice(final Person person, final Bike bike, final LocalDateTime returnTime) {
final List<RentalInfo> rentalInfos = bike.getRentalInfos();
int price = 0;
LocalDateTime lastEndTime = null;
for (final RentalInfo info : rentalInfos) {
if (lastEndTime == null) {
price = pricePrRental + pricePrHour * computeHours(info.getStartTime(), returnTime);
} else if (info.getEndTime().compareTo(lastEndTime) > 0) {
price += pricePrExtension;
if (info.getStartTime().compareTo(lastEndTime) > 0) {
price += pricePrLate;
}
}
lastEndTime = info.getEndTime();
}
if (returnTime.compareTo(lastEndTime) > 0) {
price += pricePrExtension;
if (returnTime.compareTo(lastEndTime) > 0) {
price += pricePrLate;
}
}
return price;
}
private int computeHours(final LocalDateTime startTime, final LocalDateTime endTime) {
return (int) Duration.between(startTime, endTime).toHours() + 1;
}
}
package bike;
public class GeoLocation {
private final double latitude;
private final double longitude;
public GeoLocation(final double latitude, final double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
/**
* Computes distance to other Location
* @param other
* @return distance to other Location
*/
public double distance(final GeoLocation other) {
return distance(latitude, longitude, other.latitude, other.longitude);
}
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
/*:: :*/
/*:: This routine calculates the distance between two points (given the :*/
/*:: latitude/longitude of those points). It is being used to calculate :*/
/*:: the distance between two locations using GeoDataSource (TM) prodducts :*/
/*:: :*/
/*:: Definitions: :*/
/*:: South latitudes are negative, east longitudes are positive :*/
/*:: :*/
/*:: Passed to function: :*/
/*:: lat1, lon1 = Latitude and Longitude of point 1 (in decimal degrees) :*/
/*:: lat2, lon2 = Latitude and Longitude of point 2 (in decimal degrees) :*/
/*:: Worldwide cities and other features databases with latitude longitude :*/
/*:: are available at http://www.geodatasource.com :*/
/*:: :*/
/*:: For enquiries, please contact sales@geodatasource.com :*/
/*:: :*/
/*:: Official Web site: http://www.geodatasource.com :*/
/*:: :*/
/*:: GeoDataSource.com (C) All Rights Reserved 2015 :*/
/*:: :*/