Add oving0
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -14,11 +14,4 @@ target/
|
||||
out/
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
# .classpath
|
||||
|
||||
# Include directories for initial commit
|
||||
src/main/resources/*
|
||||
!src/main/resources/.gitkeep
|
||||
|
||||
src/test/java/*
|
||||
!src/test/java/.gitkeep
|
||||
# .classpath
|
||||
66
oppgavetekster/oving0/README.md
Normal file
66
oppgavetekster/oving0/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Øving 0: Oppsett av Java
|
||||
|
||||
Denne øvingen er valgfri og ikke en del av det tellende øvingsopplegget. Øvingen er ment som en introduksjon til Java og hvordan du kan komme i gang med å programmere i Java. Du skal installere Java Development Kit (JDK) og kjøre enkle Java-programmer. Du skal også lære grunnleggende forskjell på Java og Python, og oversette Python-kode til Java.
|
||||
|
||||
## Øvingsmål
|
||||
|
||||
- Intallere Java Development kit
|
||||
- Installere og sette opp VS Code og Git
|
||||
- Lære grunnleggende forskjell på Java og Python, og oversette Python-kode til Java
|
||||
- Kjøre og kompilere Java-kode
|
||||
- Kjøre enhetstester for å sjekke at koden fungerer som den skal
|
||||
|
||||
### Del 1: Installere JDK og VS Code
|
||||
|
||||
For å kunne programmere i Java må du installere Java Development Kit (JDK). Vi kommer til å bruke VS Code som koderedigeringsverktøy i TDT4100.
|
||||
|
||||
- Gå inn på [denne siden](https://www.ntnu.no/wiki/x/Fgb6DQ) og følg instruksjonene for å installere JDK og VS Code. Husk å følge instruksjonene for ditt operativsystem. **På grunn av trøbbel med tilgang til å redigere wiki er denne litt utdatert. Den anbefaler Java 21, men vi kommer til å bruke Java 23. Dere finner den [her](https://adoptium.net/temurin/releases/?version=23&os=any).**
|
||||
|
||||
### Del 2: Sette opp øvingsprosjektet
|
||||
|
||||
For å kunne gjøre øvingene i TDT4100 må du sette opp et prosjekt i VS Code. Dette gjør du ved å følge instruksjonene på [denne siden](https://www.ntnu.no/wiki/x/Ggb6DQ). Husk å følge instruksjonene for ditt operativsystem. **På grunn av samme feil som nevnt i forrige del vil også denne siden være litt utdatert. Dere vil ikke klone fra "<https://gitlab.stud.idi.ntnu.no/tdt4100/v2024/tdt4100-students-24>", men fra [https://git.ntnu.no/tdt4100/tdt4100-ovinger-25](https://git.ntnu.no/tdt4100/tdt4100-ovinger-25).**
|
||||
|
||||
### Del 3: Hello world
|
||||
|
||||
For å teste at du har installert JDK og VS Code riktig, og satt opp prosjektet riktig, skal du nå kjøre et enkelt program som skriver ut "Hello world!" til konsollen.
|
||||
|
||||
- Åpne øvingsprosjektet i VS Code. Sørg for at mappen `tdt4100-ovinger-25` er den ytteste mappen i VS Code:
|
||||

|
||||
|
||||
- Åpne filen [src/main/java/oving0/HelloWorld.java](../../src/main/java/oving0/HelloWorld.java).
|
||||
Denne filen inneholder et enkelt program som skriver ut "Hello world!" til konsollen. Klikk på "Run" knappen i VS Code for å kjøre programmet. Du skal nå se "Hello world!" i konsollen:
|
||||

|
||||
|
||||
### Del 4: JavaFX og SceneBuilder
|
||||
|
||||
#### JavaFX
|
||||
|
||||
For å teste at JavaFX er installert riktig, skal du nå kjøre et enkelt program som bruker JavaFX. Åpne filen [src/main/java/oving0/todolist/fxui/TodoApp.java](../../src/main/java/oving0/todolist/fxui/TodoApp.java). Klikk på "Run" knappen i VS Code for å kjøre programmet. Du skal nå se en todolist applikasjon. Hvis du får en feilmelding i VS Code, prøv å trykke ctrl/cmd+shift+p og skriv "Java: Clean the Java language server workspace" og trykk enter. Hvis du fortsatt får feilmelding, prøv å lukke VS Code og åpne det på nytt.
|
||||
|
||||
#### SceneBuilder
|
||||
|
||||
Følg instruksjonene på [denne siden](https://www.ntnu.no/wiki/x/LAMxDg) for å installere SceneBuilder. Scene Builder er et verktøy som kan brukes til å designe brukergrensesnitt for JavaFX-applikasjoner. SceneBuilder er ikke nødvendig for å gjøre øvingene, men det vil være nyttig i prosjektet.
|
||||
|
||||
For å teste at SceneBuilder er installert riktig, skal du nå åpne todolist-applikasjonen i Scene Builder. Åpne SceneBuilder programmet, og klikk "Open Project". Naviger frem til filen [src/main/resources/oving0/todolist/fxui/Todo.fxml](../../src/main/resources/oving0/todolist/fxui/Todo.fxml) og velg den. Du skal nå se todolist-applikasjonen i Scene Builder:
|
||||
|
||||

|
||||
|
||||
### Del 5: Kjøre enhetstester
|
||||
|
||||
Enhetstester er en måte å sjekke at koden fungerer som den skal. I øvingene i TDT4100 følger det med enhetstester for hver oppgave. For å sjekke at prosjetet ditt er satt opp riktig, skal du kjøre enhetstester for Hello world-programmet.
|
||||
|
||||
- Åpne filen [/src/test/java/oving0/HelloWorldTest.java](../../src/test/java/oving0/HelloWorldTest.java). Denne filen inneholder enhetstester for Hello world-programmet. For å kjøre alle testene klikker man på den øverste grønne dobbel-pilen i VS Code. Man kan også kjøre en og en test ved å klikke på den grønne enkelt-pilen ved siden av hver test:
|
||||

|
||||
|
||||
- Etter å ha kjørt testene skal du se at alle testene er grønne, og at det står 2/2 tests passed i vinduet som åpner seg til venstre:
|
||||

|
||||
|
||||
### Del 6: Java vs Python
|
||||
|
||||
I denne delen skal du lære grunnleggende forskjell på Java og Python, og oversette Python-kode til Java. Fortsett i [denne filen](./python_vs_java.md).
|
||||
|
||||
### Del 7: Videre lesing
|
||||
|
||||
Har du kommet til denne delen er du nok klar for å starte på de ordentlige øvingene. Vi anbefaler også at du blir litt kjent med Wiki-sidene, spesielt [Objektorientert programmering](https://www.ntnu.no/wiki/x/wRzuAw), [Java programmering](https://www.ntnu.no/wiki/x/zx3uAw) og [Prosedyreorientert programmering](https://www.ntnu.no/wiki/x/qx3uAw). Det er ikke forventet at dere skal lese alt som står på alle undersidene her, men det er greit å ha en oversikt over hva som er der, så vet dere hvor dere kan finne informasjon om dere trenger det.
|
||||
|
||||
Lykke til med øvingene!
|
||||
BIN
oppgavetekster/oving0/img/Passed_tests.png
Normal file
BIN
oppgavetekster/oving0/img/Passed_tests.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
BIN
oppgavetekster/oving0/img/helloWorld.png
Normal file
BIN
oppgavetekster/oving0/img/helloWorld.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
BIN
oppgavetekster/oving0/img/kjor_test.png
Normal file
BIN
oppgavetekster/oving0/img/kjor_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
BIN
oppgavetekster/oving0/img/opp1.png
Normal file
BIN
oppgavetekster/oving0/img/opp1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
BIN
oppgavetekster/oving0/img/prosjektmappe.png
Normal file
BIN
oppgavetekster/oving0/img/prosjektmappe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
BIN
oppgavetekster/oving0/img/scenebuilder.png
Normal file
BIN
oppgavetekster/oving0/img/scenebuilder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
259
oppgavetekster/oving0/python_vs_java.md
Normal file
259
oppgavetekster/oving0/python_vs_java.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# Python VS Java
|
||||
|
||||
Noe av det første du kommer til å merke når du skal programmere i Java er at det kreves en del flere linjer kode enn i Python. La oss starte med noe av det enkleste, å skrive ut en tekst til konsollen.
|
||||
|
||||
## Skrive ut tekst til konsollen
|
||||
|
||||
I Python er det veldig greit å skrive ut tekst til konsollen. Du trenger bare å skrive `print("tekst")` og så vil teksten skrives ut til konsollen. I Java trengs det litt bokstaver:
|
||||
|
||||
**Python**:
|
||||
|
||||
```python
|
||||
print("Hello World!")
|
||||
```
|
||||
|
||||
**Java**:
|
||||
|
||||
```java
|
||||
System.out.println("Hello World!");
|
||||
```
|
||||
|
||||
En snarvei i VS Code for å slippe å skrive hele `System.out.println()` er å skrive `sout` og trykke `tab`-tasten. Da vil hele koden bli skrevet ut for deg.
|
||||
|
||||
## Variabler
|
||||
|
||||
I Python er det ganske rett frem å lage variabler. Du trenger bare å skrive `variabelnavn = verdi`. I Java må du først angi hvilken type variabelen skal være. De vanligste typene er `int`, `double`, `boolean` og `String`. `double` er et desimaltall (samme som `float` i python), `boolean` er en `true` eller `false`-verdi og `String` er en tekststreng.
|
||||
|
||||
La oss se på et eksempel:
|
||||
|
||||
**Python**:
|
||||
|
||||
```python
|
||||
x = 5
|
||||
y = 10.6
|
||||
s = "hei"
|
||||
ja = True
|
||||
```
|
||||
|
||||
**Java**:
|
||||
|
||||
```java
|
||||
int x = 5;
|
||||
double y = 10.6;
|
||||
String s = "hei";
|
||||
boolean ja = true;
|
||||
```
|
||||
|
||||
Noen ting som er viktig å merke seg:
|
||||
|
||||
- I Java må man ha semikolon på slutten av hver linje der man deklarerer variabler (og mange andre steder, men det kommer vi tilbake til senere).
|
||||
|
||||
- Boolske verdier skrives med liten forbokstav i Java (`true`, `false`), mens de skrives med stor forbokstav i Python.
|
||||
|
||||
## Oppgave 1
|
||||
|
||||
Nå skal du lage et program som ganger sammen to tall og skriver ut resultatet til konsollen. Bruk variabler til å lagre tallene. Bruk `System.out.println()` til å skrive ut resultatet.
|
||||
|
||||
- Lag en ny fil ved å høyreklikke på `src/main/java/oving0`-mappen og velg `New file...`. Gi filen et navn som slutter på `.java`, for eksempel `Oppgave1.java`. Når du trykker Enter vil du få opp den nye filen og noe forhåndsutfylt kode:
|
||||

|
||||
Velg `class`.
|
||||
|
||||
- For å kjøre kode i Java må man lage en `main`-metode. Denne ser slik ut:
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
// Kode her
|
||||
}
|
||||
```
|
||||
|
||||
- Enten kopier koden over, eller skriv `main` og trykk `tab`-tasten for å auto-fylle koden.
|
||||
|
||||
- Lag to variabler som inneholder tallene du skal gange sammen. Du kan kalle dem `x` og `y` eller noe annet du vil. Velg selv om du vil bruke `int` eller `double`.
|
||||
|
||||
- Lag en variabel som inneholder resultatet av gangeoperasjonen. Du kan kalle den `z` eller noe annet du vil.
|
||||
|
||||
- Skriv ut resultatet til konsollen ved å bruke `System.out.println();`.
|
||||
|
||||
## `if`-setninger
|
||||
|
||||
Nå skal vi se på forskjellen mellom `if`-setninger i Python og Java:
|
||||
|
||||
**Python**:
|
||||
|
||||
```python
|
||||
if betingelse1 or not betingelse2:
|
||||
# Kode her
|
||||
elif betingelse1 and betingelse2:
|
||||
# Kode her
|
||||
else:
|
||||
# Kode her
|
||||
```
|
||||
|
||||
**Java**:
|
||||
|
||||
```java
|
||||
if (betingelse1 || !betingelse2) {
|
||||
// Kode her
|
||||
}
|
||||
else if (betingelse1 && betingelse2) {
|
||||
// Kode her
|
||||
}
|
||||
else {
|
||||
// Kode her
|
||||
}
|
||||
```
|
||||
|
||||
Viktige forskjeller å merke seg:
|
||||
|
||||
- Python bruker kolon (`:`) etter if-setningen, mens Java bruker krøllparenteser (`{}`).
|
||||
|
||||
- Python bruker `elif` for å si at det er en `else if`-setning, mens Java bruker `else if`.
|
||||
|
||||
- Python bruker innrykk for å si at noe kode skal være inni if-setningen, mens Java bruker krøllparenteser.
|
||||
|
||||
- Python bruker `and` og `or`, mens Java bruker `&&` og `||` for å si at to betingelser skal være sant samtidig eller at en av betingelsene skal være sant.
|
||||
|
||||
- Python bruker `not` for å si at en betingelse skal være falsk, mens Java bruker `!`.
|
||||
|
||||
- Java krever at betingelsene er omgitt av parenteser.
|
||||
|
||||
## Oppgave 2
|
||||
|
||||
Velg om du vil lage en ny fil eller bruke den du lagde i forrige oppgave. Skriv koden i en `main`-metode.
|
||||
Oversett koden under fra Python til Java:
|
||||
|
||||
```python
|
||||
x = 3
|
||||
y = 5
|
||||
|
||||
if x > 5 and y < 10:
|
||||
print("x er større enn 5 og y er mindre enn 10")
|
||||
elif x > 5 or y < 10:
|
||||
print("x er større enn 5 eller y er mindre enn 10")
|
||||
else:
|
||||
print("x er mindre enn 5 og y er større enn 10")
|
||||
```
|
||||
|
||||
## Løkker
|
||||
|
||||
Nå skal vi se på forskjellen mellom løkker i Python og Java:
|
||||
|
||||
**Python**:
|
||||
|
||||
```python
|
||||
for i in range(10):
|
||||
# Kode her
|
||||
|
||||
while betingelse:
|
||||
# Kode her
|
||||
```
|
||||
|
||||
**Java**:
|
||||
|
||||
```java
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Kode her
|
||||
}
|
||||
|
||||
while (betingelse) {
|
||||
// Kode her
|
||||
}
|
||||
```
|
||||
|
||||
`while`-løkken er ganske lik i Python og Java. `for`-løkken kan virke litt mer skremmende, men vi skal se på den litt nærmere.
|
||||
|
||||
### For-løkker
|
||||
|
||||
En `for`-løkke i Java består av tre deler, og ligner egentlig ganske mye på en `while`-løkke. De tre delene er:
|
||||
|
||||
- En variabel som skal telle oppover eller nedover. Denne kan være av typen `int`, `double` eller `float`. Denne variabelen må være unik for løkken, det vil si at den ikke kan hete det samme som en variabel som allerede er brukt i koden.
|
||||
|
||||
- En betingelse som må være sant for at løkken skal fortsette.
|
||||
|
||||
- En operasjon som skal utføres hver gang løkken kjører. Denne operasjonen kan være å øke eller redusere variabelen som telle oppover eller nedover.
|
||||
|
||||
I eksempelet over er variabelen `i`, betingelsen `i < 10` og operasjonen `i++`. Variabelen, betingelsen og operasjonen er sepparert med semikolon (`;`). `i++` betyr at variabelen `i` skal økes med 1 hver gang løkken kjører. Man kan også øke med for eksempel `3`, og skrive `i += 3`. Startverdien til variabelen kan endres til f.eks. `5` ved å skrive `for (int i = 5; ...`.
|
||||
|
||||
## Oppgave 3
|
||||
|
||||
Velg om du vil lage en ny fil eller bruke den du lagde i forrige oppgave. Skriv koden i en `main`-metode.
|
||||
|
||||
Oversett koden under fra Python til Java:
|
||||
|
||||
```python
|
||||
for i in range(3,10):
|
||||
if i % 2 == 0:
|
||||
print(i)
|
||||
|
||||
j = 0
|
||||
while j < 10:
|
||||
print(j)
|
||||
j += 1
|
||||
```
|
||||
|
||||
## Funksjoner
|
||||
|
||||
Nå skal vi se på forskjellen mellom funksjoner i Python og Java:
|
||||
|
||||
**Python**:
|
||||
|
||||
```python
|
||||
def funksjonsnavn(parameter1, parameter2):
|
||||
# Kode her
|
||||
return resultat
|
||||
```
|
||||
|
||||
**Java**:
|
||||
|
||||
```java
|
||||
public int funksjonsnavn(int parameter1, int parameter2) {
|
||||
// Kode her
|
||||
return resultat;
|
||||
}
|
||||
```
|
||||
|
||||
De fleste funskjoner man lager i Java hører til en gitt **klasse** (ikke tenk på hva dette er akkurat nå, dette lærer dere snart). Funksjoner som hører til klasser kalles **metoder**.
|
||||
|
||||
En metode i Java har alltid en **return-type**. Return-type er typen variabelen som skal returneres fra metoden. Return-type kan være `void`, `int`, `double`, `float`, `String` eller en annen type. Hvis return-type er `void` betyr det at metoden ikke skal returnere noe. Alle parametre som skal sendes til metoden må ha en **type**. Typen til parametrene kan være `int`, `double`, `float`, `String` eller en annen type. For nå kan du ignorere at det står `public` foran metoden, dette lærer du om snart.
|
||||
|
||||
Alle funskjoner (metoder) i Java må ligge inni en klasse.
|
||||
|
||||
Eksempel på en klasse med en metode:
|
||||
|
||||
```java
|
||||
package minpakke;
|
||||
|
||||
public class KlasseNavn {
|
||||
|
||||
// Kode her
|
||||
|
||||
public int metode1(int parameter1, int parameter2) {
|
||||
// Kode her
|
||||
int resultat = parameter1 + parameter2;
|
||||
return resultat;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Oppgave 4
|
||||
|
||||
Enten lag en ny klasse, eller bruk den du brukte i forrige oppgave. Lag metoder som tilsvarer funksjonene i Python-koden under:
|
||||
|
||||
```python
|
||||
def division(x, y):
|
||||
return x / y
|
||||
|
||||
def fakultet(x):
|
||||
fak = 1
|
||||
for i in range(1, x+1):
|
||||
fak *= i
|
||||
return fak
|
||||
|
||||
def erPrimtall(x):
|
||||
if x < 2:
|
||||
return False
|
||||
for i in range(2, x):
|
||||
if x % i == 0:
|
||||
return False
|
||||
return True
|
||||
```
|
||||
16
src/main/java/oving0/HelloWorld.java
Normal file
16
src/main/java/oving0/HelloWorld.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package oving0;
|
||||
|
||||
public class HelloWorld {
|
||||
|
||||
public String getHelloWorld() {
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
public int getHelloWorldLength() {
|
||||
return this.getHelloWorld().length();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(new HelloWorld().getHelloWorld());
|
||||
}
|
||||
}
|
||||
43
src/main/java/oving0/todolist/fxui/ITodoFileReading.java
Normal file
43
src/main/java/oving0/todolist/fxui/ITodoFileReading.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import oving0.todolist.model.TodoList;
|
||||
|
||||
public interface ITodoFileReading {
|
||||
|
||||
/**
|
||||
* Read a TodoList from a given InputStream.
|
||||
*
|
||||
* @param ios The input stream to read from.
|
||||
* @return The TodoList from the InputStream.
|
||||
*/
|
||||
TodoList readTodoList(InputStream is);
|
||||
|
||||
/**
|
||||
* Read a TodoList with a given name, from a default (implementation-specific) location.
|
||||
*
|
||||
* @param name The name of the TodoList
|
||||
* @return The TodoList with the given name from the default location
|
||||
* @throws IOException if the TodoList can't be found.
|
||||
*/
|
||||
TodoList readTodoList(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Write a TodoList to a given OutputStream
|
||||
*
|
||||
* @param todoList The list to write
|
||||
* @param os The stream to write to
|
||||
*/
|
||||
void writeTodoList(TodoList todoList, OutputStream os);
|
||||
|
||||
/**
|
||||
* Write a TodoList to a file named after the list in a default (implementation specific)
|
||||
* location.
|
||||
*
|
||||
* @param todoList The list to write
|
||||
* @throws IOException If a file at the proper location can't be written to
|
||||
*/
|
||||
void writeTodoList(TodoList todoList) throws IOException;
|
||||
}
|
||||
21
src/main/java/oving0/todolist/fxui/TodoApp.java
Normal file
21
src/main/java/oving0/todolist/fxui/TodoApp.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class TodoApp extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
Parent parent = FXMLLoader.load(getClass().getResource("Todo.fxml"));
|
||||
stage.setScene(new Scene(parent));
|
||||
stage.show();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(TodoApp.class, args);
|
||||
}
|
||||
}
|
||||
238
src/main/java/oving0/todolist/fxui/TodoController.java
Normal file
238
src/main/java/oving0/todolist/fxui/TodoController.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Window;
|
||||
import oving0.todolist.model.TodoEntry;
|
||||
import oving0.todolist.model.TodoList;
|
||||
|
||||
public class TodoController {
|
||||
|
||||
private final TodoSettings todoSettings = new TodoSettings();
|
||||
|
||||
public TodoSettings getTodoSettings() {
|
||||
return todoSettings;
|
||||
}
|
||||
|
||||
private TodoList todoList;
|
||||
|
||||
public void setTodoList(final TodoList todoList) {
|
||||
this.todoList = todoList;
|
||||
updateTodoListView();
|
||||
}
|
||||
|
||||
private final ITodoFileReading fileSupport = new TodoFileSupport();
|
||||
|
||||
@FXML
|
||||
private String sampleTodoListResource;
|
||||
|
||||
@FXML
|
||||
private Text todoListNameView;
|
||||
|
||||
@FXML
|
||||
private ListView<String> todoListEntriesView;
|
||||
|
||||
@FXML
|
||||
private TextField todoEntryTextField;
|
||||
|
||||
@FXML
|
||||
private TextField fileLocationNameField;
|
||||
|
||||
@FXML
|
||||
private Button todoEntryButton;
|
||||
|
||||
@FXML
|
||||
private Text statusText;
|
||||
private String defaultStatusText;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@FXML
|
||||
public void initialize() {
|
||||
final var todoList = new TodoList();
|
||||
todoList.setName("todo list");
|
||||
setTodoList(todoList);
|
||||
todoListEntriesView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((prop, oldValue, newValue) -> {
|
||||
handleTodoListEntriesViewSelectedItemChanged();
|
||||
});
|
||||
handleTodoListEntriesViewSelectedItemChanged();
|
||||
defaultStatusText = statusText.getText();
|
||||
}
|
||||
|
||||
private void updateTodoListView() {
|
||||
todoListNameView.setText(todoList.getName());
|
||||
todoListEntriesView.getItems().setAll(getOrderedTodoListItems());
|
||||
}
|
||||
|
||||
protected List<String> getOrderedTodoListItems() {
|
||||
final List<String> listItems = new ArrayList<>();
|
||||
for (final var entry : todoList) {
|
||||
listItems.add(entry.getText());
|
||||
}
|
||||
switch (todoSettings.getTodoListOrder()) {
|
||||
case ADD_ORDER_REVERSED: {
|
||||
Collections.reverse(listItems);
|
||||
break;
|
||||
}
|
||||
case LEXICOGRAPHIC_ORDER: {
|
||||
Collections.sort(listItems);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
return listItems;
|
||||
}
|
||||
|
||||
private void handleTodoListEntriesViewSelectedItemChanged() {
|
||||
todoEntryTextField.setText(todoListEntriesView.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleTodoEntryTextFieldChange(final StringProperty prop, final String oldValue,
|
||||
final String newValue) {
|
||||
if (todoList.hasEntry(newValue)) {
|
||||
todoEntryButton.setText("Remove");
|
||||
} else {
|
||||
todoEntryButton.setText("Add");
|
||||
}
|
||||
todoEntryButton.setDisable(newValue == null || newValue.isBlank());
|
||||
}
|
||||
|
||||
private FileChooser fileChooser;
|
||||
|
||||
private String getFileLocationName(final boolean isSave) {
|
||||
if (fileChooser == null) {
|
||||
fileChooser = new FileChooser();
|
||||
}
|
||||
final Window window = fileLocationNameField.getScene().getWindow();
|
||||
File file = null;
|
||||
if (isSave) {
|
||||
file = fileChooser.showSaveDialog(window);
|
||||
} else {
|
||||
file = fileChooser.showOpenDialog(window);
|
||||
}
|
||||
if (file != null) {
|
||||
String name = file.getName();
|
||||
final int pos = name.lastIndexOf('.');
|
||||
if (pos >= 0) {
|
||||
name = name.substring(0, pos);
|
||||
}
|
||||
if (!name.isBlank()) {
|
||||
return file.getParent() + File.separator + name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleBrowseButtonAction() {
|
||||
final String name = getFileLocationName(false);
|
||||
if (name != null) {
|
||||
fileLocationNameField.setText(name);
|
||||
}
|
||||
}
|
||||
|
||||
private String ensureFileLocation(final boolean isSave) {
|
||||
String name = fileLocationNameField.getText();
|
||||
if (name.isBlank()) {
|
||||
name = getFileLocationName(false);
|
||||
if (name != null) {
|
||||
fileLocationNameField.setText(name);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleLoadButtonAction() {
|
||||
final String name = ensureFileLocation(false);
|
||||
if (!name.isBlank()) {
|
||||
try {
|
||||
setTodoList(fileSupport.readTodoList(name));
|
||||
statusText.setText(defaultStatusText);
|
||||
} catch (final IOException e) {
|
||||
statusText.setText(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleSaveButtonAction() {
|
||||
final String name = ensureFileLocation(true);
|
||||
if (!name.isBlank()) {
|
||||
try {
|
||||
todoList.setName(name);
|
||||
fileSupport.writeTodoList(todoList);
|
||||
todoListNameView.setText(todoList.getName());
|
||||
statusText.setText(defaultStatusText);
|
||||
} catch (final IOException e) {
|
||||
statusText.setText(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleTodoEntryButtonAction() {
|
||||
final var text = todoEntryTextField.getText();
|
||||
final var entry = todoList.findEntry(text);
|
||||
if (entry != null) {
|
||||
todoList.removeEntry(entry);
|
||||
} else {
|
||||
todoList.addEntry(new TodoEntry(text));
|
||||
}
|
||||
todoEntryTextField.clear();
|
||||
updateTodoListView();
|
||||
}
|
||||
|
||||
private TodoSettingsController settingsController;
|
||||
|
||||
private Scene scene;
|
||||
private Parent oldSceneRoot, settingsSceneRoot;
|
||||
|
||||
@FXML
|
||||
private void handleSettingsAction() {
|
||||
// check if we need to load settings ui from fxml
|
||||
if (settingsController == null) {
|
||||
final FXMLLoader fxmlLoader =
|
||||
new FXMLLoader(getClass().getResource("TodoSettings.fxml"));
|
||||
try {
|
||||
// load settings ui
|
||||
settingsSceneRoot = fxmlLoader.load();
|
||||
// remember old ui
|
||||
scene = todoListEntriesView.getScene();
|
||||
oldSceneRoot = scene.getRoot();
|
||||
// get the settings controller, so we can set its todoSettings property
|
||||
settingsController = fxmlLoader.getController();
|
||||
settingsController.setTodoController(this);
|
||||
} catch (final IOException e) {
|
||||
}
|
||||
}
|
||||
if (settingsController != null) {
|
||||
todoListEntriesView.getScene().setRoot(settingsSceneRoot);
|
||||
settingsController.setTodoSettings(todoSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyTodoSettings(final TodoSettings settings) {
|
||||
if (settings != null) {
|
||||
settings.copyInto(this.todoSettings);
|
||||
}
|
||||
scene.setRoot(oldSceneRoot);
|
||||
if (settings != null) {
|
||||
updateTodoListView();
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/main/java/oving0/todolist/fxui/TodoFileSupport.java
Normal file
80
src/main/java/oving0/todolist/fxui/TodoFileSupport.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Scanner;
|
||||
import oving0.todolist.model.TodoEntry;
|
||||
import oving0.todolist.model.TodoList;
|
||||
|
||||
public class TodoFileSupport implements ITodoFileReading {
|
||||
|
||||
public static final String TODO_EXTENSION = "todo";
|
||||
|
||||
private Path getTodoUserFolderPath() {
|
||||
return Path.of(System.getProperty("user.home"), "tdt4100", "todo");
|
||||
}
|
||||
|
||||
private boolean ensureTodoUserFolder() {
|
||||
try {
|
||||
Files.createDirectories(getTodoUserFolderPath());
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Path getTodoListPath(String name) {
|
||||
return getTodoUserFolderPath().resolve(name + "." + TODO_EXTENSION);
|
||||
}
|
||||
|
||||
public TodoList readTodoList(InputStream input) {
|
||||
TodoList todoList = null;
|
||||
try (var scanner = new Scanner(input)) {
|
||||
todoList = new TodoList();
|
||||
while (scanner.hasNextLine()) {
|
||||
var line = scanner.nextLine().stripTrailing();
|
||||
if (line.isEmpty() || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
if (todoList.getName() == null) {
|
||||
todoList.setName(line);
|
||||
} else {
|
||||
todoList.addEntry(new TodoEntry(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
return todoList;
|
||||
}
|
||||
|
||||
public TodoList readTodoList(String name) throws IOException {
|
||||
var todoListPath = getTodoListPath(name);
|
||||
try (var input = new FileInputStream(todoListPath.toFile())) {
|
||||
return readTodoList(input);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTodoList(TodoList todoList, OutputStream output) {
|
||||
try (var writer = new PrintWriter(output)) {
|
||||
writer.println("# name");
|
||||
writer.println(todoList.getName());
|
||||
writer.println("# entries");
|
||||
for (var entry : todoList) {
|
||||
writer.println(entry.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTodoList(TodoList todoList) throws IOException {
|
||||
var todoListPath = getTodoListPath(todoList.getName());
|
||||
ensureTodoUserFolder();
|
||||
try (var output = new FileOutputStream(todoListPath.toFile())) {
|
||||
writeTodoList(todoList, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/main/java/oving0/todolist/fxui/TodoLauncher.java
Normal file
11
src/main/java/oving0/todolist/fxui/TodoLauncher.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
/**
|
||||
* Class needed in a shaded über jar to run a JavaFX app
|
||||
*/
|
||||
public class TodoLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
TodoApp.main(args);
|
||||
}
|
||||
}
|
||||
38
src/main/java/oving0/todolist/fxui/TodoSettings.java
Normal file
38
src/main/java/oving0/todolist/fxui/TodoSettings.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
public class TodoSettings {
|
||||
|
||||
public enum TodoListOrder {
|
||||
ADD_ORDER, ADD_ORDER_REVERSED, LEXICOGRAPHIC_ORDER;
|
||||
}
|
||||
|
||||
private TodoListOrder todoListOrder = TodoListOrder.ADD_ORDER;
|
||||
|
||||
public TodoSettings() {}
|
||||
|
||||
/**
|
||||
* Initialises this TodoSettings from the provided argument
|
||||
*
|
||||
* @param target the target TodoSettings
|
||||
*/
|
||||
public TodoSettings(final TodoSettings todoSettings) {
|
||||
todoSettings.copyInto(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all properties in this TodoSettings into target
|
||||
*
|
||||
* @param target the target TodoSettings
|
||||
*/
|
||||
public void copyInto(final TodoSettings target) {
|
||||
target.setTodoListOrder(this.getTodoListOrder());
|
||||
}
|
||||
|
||||
public TodoListOrder getTodoListOrder() {
|
||||
return todoListOrder;
|
||||
}
|
||||
|
||||
public void setTodoListOrder(final TodoListOrder todoListOrder) {
|
||||
this.todoListOrder = todoListOrder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
|
||||
public class TodoSettingsController {
|
||||
|
||||
private TodoController todoController;
|
||||
|
||||
public void setTodoController(final TodoController todoController) {
|
||||
this.todoController = todoController;
|
||||
}
|
||||
|
||||
private TodoSettings todoSettings = new TodoSettings();
|
||||
|
||||
public void setTodoSettings(final TodoSettings todoSettings) {
|
||||
this.todoSettings = todoSettings;
|
||||
updateView();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private ChoiceBox<String> listOrderSelector;
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
// same order as TodoSettings.ListOrder
|
||||
listOrderSelector.getItems().setAll("Add order", "Add order reversed", "Alphabetic");
|
||||
updateView();
|
||||
}
|
||||
|
||||
private void updateView() {
|
||||
listOrderSelector.getSelectionModel().select(todoSettings.getTodoListOrder().ordinal());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void handleApplySettings() {
|
||||
todoController.applyTodoSettings(todoSettings);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void handleCancelSettings() {
|
||||
todoController.applyTodoSettings(null);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void handleListOrderSelection() {
|
||||
this.todoSettings.setTodoListOrder(TodoSettings.TodoListOrder.values()[listOrderSelector
|
||||
.getSelectionModel().getSelectedIndex()]);
|
||||
}
|
||||
}
|
||||
31
src/main/java/oving0/todolist/model/TodoEntry.java
Normal file
31
src/main/java/oving0/todolist/model/TodoEntry.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package oving0.todolist.model;
|
||||
|
||||
/**
|
||||
* Class representing a single entry in a To-Do-list.
|
||||
*
|
||||
* The class contains a text-value representing the label of the entry, and has a getText-method to
|
||||
* retrieve said label.
|
||||
*
|
||||
*/
|
||||
public class TodoEntry {
|
||||
|
||||
private final String text;
|
||||
|
||||
/**
|
||||
* Create a new entry with a given label.
|
||||
*
|
||||
* @param text The label to assign to this entry
|
||||
*/
|
||||
public TodoEntry(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[TodoEntry \"%s\"]", getText());
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
81
src/main/java/oving0/todolist/model/TodoList.java
Normal file
81
src/main/java/oving0/todolist/model/TodoList.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package oving0.todolist.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A TodoList, which is an iterable of {@link TodoEntry}s.
|
||||
*
|
||||
* The class has a name, along with methods for adding and removing entries, searching through them,
|
||||
* and checking if one exists in the list.
|
||||
*/
|
||||
public class TodoList implements Iterable<TodoEntry> {
|
||||
|
||||
private String name;
|
||||
|
||||
private List<TodoEntry> entries = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[TodoList \"%s\" with %s entries]", getName(), entries.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<TodoEntry> iterator() {
|
||||
return entries.iterator();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an entry given its text label.
|
||||
*
|
||||
* @param text The label to look for.
|
||||
* @return An entry with the given text label if it exists, otherwise null.
|
||||
*/
|
||||
public TodoEntry findEntry(String text) {
|
||||
for (var entry : entries) {
|
||||
if (entry.getText().equals(text)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this TodoList has an entry with a given text label.
|
||||
*
|
||||
* @param text The text label of the entry
|
||||
* @return true if an entry with the given text label is in this TodoList, otherwise false
|
||||
*/
|
||||
public boolean hasEntry(String text) {
|
||||
return findEntry(text) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to this TodoList.
|
||||
*
|
||||
* @param entry The entry to add.
|
||||
*/
|
||||
public void addEntry(TodoEntry entry) {
|
||||
if (!entries.contains(entry)) {
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and entry from this TodoList.
|
||||
*
|
||||
* @param entry The entry to remove.
|
||||
*/
|
||||
public void removeEntry(TodoEntry entry) {
|
||||
entries.remove(entry);
|
||||
}
|
||||
}
|
||||
40
src/main/resources/oving0/todolist/fxui/Todo.fxml
Normal file
40
src/main/resources/oving0/todolist/fxui/Todo.fxml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
|
||||
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="oving0.todolist.fxui.TodoController">
|
||||
<fx:define>
|
||||
<String fx:id="sampleTodoListResource" fx:value="sample-todolist.todo" />
|
||||
</fx:define>
|
||||
<Text fx:id="todoListNameView" text="<the todo list name>" />
|
||||
<ListView fx:id="todoListEntriesView" />
|
||||
<HBox>
|
||||
<TextField fx:id="todoEntryTextField" onTextChange="#handleTodoEntryTextFieldChange"
|
||||
onAction="#handleTodoEntryButtonAction" />
|
||||
<Button fx:id="todoEntryButton" onAction="#handleTodoEntryButtonAction" />
|
||||
</HBox>
|
||||
<Pane prefHeight="10" />
|
||||
<Text text="Save/Load" style="-fx-font-size: 20;" />
|
||||
<HBox>
|
||||
<Text text="File location:" />
|
||||
<TextField fx:id="fileLocationNameField" />
|
||||
<Button onAction="#handleBrowseButtonAction" text="Browse.." />
|
||||
</HBox>
|
||||
<HBox>
|
||||
<Button onAction="#handleLoadButtonAction" text="Load" />
|
||||
<Button onAction="#handleSaveButtonAction" text="Save" />
|
||||
</HBox>
|
||||
<Pane prefHeight="10" />
|
||||
<HBox>
|
||||
<Button onAction="#handleSettingsAction" text="Settings..." />
|
||||
</HBox>
|
||||
<Pane prefHeight="10" />
|
||||
<Text fx:id="statusText" text="OK" />
|
||||
</VBox>
|
||||
22
src/main/resources/oving0/todolist/fxui/TodoSettings.fxml
Normal file
22
src/main/resources/oving0/todolist/fxui/TodoSettings.fxml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
|
||||
<VBox xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="oving0.todolist.fxui.TodoSettingsController">
|
||||
<GridPane>
|
||||
<Label text="List order" GridPane.rowIndex="0" GridPane.columnIndex="0" />
|
||||
<ChoiceBox fx:id="listOrderSelector" GridPane.rowIndex="0" GridPane.columnIndex="1"
|
||||
onAction="#handleListOrderSelection" />
|
||||
</GridPane>
|
||||
<HBox>
|
||||
<Button text="Apply" onAction="#handleApplySettings" />
|
||||
<Button text="Cancel" onAction="#handleCancelSettings" />
|
||||
</HBox>
|
||||
</VBox>
|
||||
@@ -0,0 +1,4 @@
|
||||
# name
|
||||
sample todo list
|
||||
Øl
|
||||
Potetgull
|
||||
35
src/test/java/oving0/EnvironmentTest.java
Normal file
35
src/test/java/oving0/EnvironmentTest.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package oving0;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* This test checks if you have the correct version of Java installed.
|
||||
*/
|
||||
public class EnvironmentTest {
|
||||
|
||||
private static final int RECOMMENDED_JAVA_VERSION = 25;
|
||||
|
||||
@Test
|
||||
public void testJavaVersion() {
|
||||
int javaVersionNumber = this.getVersion();
|
||||
assertEquals(RECOMMENDED_JAVA_VERSION, javaVersionNumber,
|
||||
"Wrong Java version! We recommend that you use " + RECOMMENDED_JAVA_VERSION);
|
||||
}
|
||||
|
||||
private int getVersion() {
|
||||
String version = System.getProperty("java.version");
|
||||
|
||||
if (version.startsWith("1.")) {
|
||||
version = version.substring(2, 3);
|
||||
} else {
|
||||
int dot = version.indexOf(".");
|
||||
|
||||
if (dot != -1) {
|
||||
version = version.substring(0, dot);
|
||||
}
|
||||
}
|
||||
|
||||
return Integer.parseInt(version);
|
||||
}
|
||||
}
|
||||
25
src/test/java/oving0/HelloWorldTest.java
Normal file
25
src/test/java/oving0/HelloWorldTest.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package oving0;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class HelloWorldTest {
|
||||
|
||||
private HelloWorld helloWorld;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
helloWorld = new HelloWorld();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloWorld() {
|
||||
assertEquals("Hello World!", helloWorld.getHelloWorld());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloWorldLength() {
|
||||
assertEquals(12, helloWorld.getHelloWorldLength());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user