Skip to content
Snippets Groups Projects
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
No related branches found
No related tags found
No related merge requests found
Showing
with 749 additions and 0 deletions
<?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 :*/
/*:: :*/
/*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
public static double distance(final double lat1, final double lon1, final double lat2, final double lon2) {
final double theta = lon1 - lon2;
double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
dist = Math.acos(dist);
// convert to degrees
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
// convert to meters
dist = dist * 1609.344;
return dist;
}
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
/*:: This function converts decimal degrees to radians :*/
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
private static double deg2rad(final double deg) {
return (deg * Math.PI / 180.0);
}
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
/*:: This function converts radians to decimal degrees :*/
/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
private static double rad2deg(final double rad) {
return (rad * 180 / Math.PI);
}
}
package bike;
public class Person {
private final String name;
public Person(final String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* Withdraws the provided amount from this person's account.
* If the transaction couldn't be completed, i.e. no money was actually withdrawn,
* one of the indicated exceptions will be thrown.
* @param amount the amount to withdraw
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
public void withdraw(final double amount) throws IllegalArgumentException, IllegalStateException {
// ...
}
private PricePolicy pricePolicy;
public PricePolicy getPricePolicy() {
return pricePolicy;
}
public void setPricePolicy(final PricePolicy pricePolicy) {
this.pricePolicy = pricePolicy;
}
}
package bike;
import java.time.LocalDateTime;
public interface PricePolicy {
public int computePrice(Person person, Bike bike, LocalDateTime returnTime);
}
package bike;
import java.time.LocalDateTime;
public class RentalInfo {
private final LocalDateTime startTime;
private final LocalDateTime endTime;
public RentalInfo(final LocalDateTime startTime, final LocalDateTime endTime) {
this.startTime = startTime;
this.endTime = endTime;
}
public LocalDateTime getStartTime() {
return startTime;
}
public LocalDateTime getEndTime() {
return endTime;
}
}
# Bike rental app
Temaet for oppgaven er administrasjon av sykkelutleie. Sykkelutleietjenesten [BikeRental](BikeRental.java) tilbyr leie av sykler (Bike)[Bike.java] som plukkes opp og settes tilbake på utvalgte stasjoner spredt rundt omkring. Syklene har GPS, slik at de hele tiden spores (GeoLocation)[GeoLocation.java].
Den som skal leie sykkel (Person)[Person.java] bruker en app for å få oversikt over hvor (ved hvilke stasjoner) det er tilgjengelige sykler. Deretter er det bare å identifisere sykkelen, angi hvor lenge man ønsker å leie den, og en kan sykle av gårde. Når en er ferdig med å bruke sykkelen, må den settes tilbake på en stasjon (ikke nødvendigvis den samme som en tok den fra). Ved hjelp av appen angir man at leieforholdet er avsluttet. Leieprisen blir da beregnet og pengene trukket.
Prisen er basert på påbegynte timer, og merk at en ikke betaler for mer enn faktisk bruk. Hvis man altså angir at en ønsker å leie en sykkel i tre timer, men leverer den tilbake etter en halv time, så betaler en for én times bruk. Det er mulig å utvide leietiden underveis, noe som utløser et lite gebyr. Hvis en utvider leietiden eller leverer sykkelen etter at sykkelen skulle vært levert, så påløper det også et gebyr.
kont2019/src/bike/bikemap.png

240 KiB

module kont2019 {
requires javafx.fxml;
requires javafx.graphics;
requires javafx.controls;
exports bike;
opens bike to javafx.fxml;
}
package stuff;
import java.util.Arrays;
import java.util.Iterator;
public class EverySecondMetaIterator implements Iterator<String> {
private final Iterator<String> strings;
public EverySecondMetaIterator(final Iterator<String> strings) {
this.strings = strings;
}
private String next;
@Override
public boolean hasNext() {
if (! strings.hasNext()) {
return false;
}
// store next element
next = strings.next();
// and skip the next one
strings.next();
return true;
}
@Override
public String next() {
// use stored element
return next;
}
public static void main(final String[] args) {
// Should print
// 1
// 3
// This seems to work, doesn't it!?
final EverySecondMetaIterator metaIterator = new EverySecondMetaIterator(Arrays.asList("1", "2", "3", "4").iterator());
while (metaIterator.hasNext()) {
System.out.println(metaIterator.next());
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment