Initial commit

This commit is contained in:
github-classroom[bot]
2022-04-28 08:39:46 +00:00
commit 2a34df16f4
47 changed files with 6848 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
/*
* Gradle build configuration for specific lab module / exercise
*/
// enabled plugins
plugins {
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
}
// Project/Module information
description = 'Lab05 ByteCharStream'
group = 'ch.zhaw.prog2'
version = '2022.1'
// Dependency configuration
repositories {
mavenCentral()
}
dependencies {
}
// Configuration for Application plugin
application {
// Define the main class for the application.
mainClass = 'ch.zhaw.prog2.io.FileCopy'
}
// enable console input when running with gradle
run {
standardInput = System.in
}
// Java plugin configuration
java {
// By default the Java version of the gradle process is used as source/target version.
// This can be overridden, to ensure a specific version. Enable only if required.
// sourceCompatibility = JavaVersion.VERSION_17 // ensure Java source code compatibility
// targetCompatibility = JavaVersion.VERSION_17 // version of the created byte-code
// Java compiler specific options
compileJava {
// source files should be UTF-8 encoded
options.encoding = 'UTF-8'
// for more options see https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html
}
}
+30
View File
@@ -0,0 +1,30 @@
ENGINE
Engine 449cc, 4-stroke, liquid-cooled, single cylinder, DOHC
Bore Stroke 96.0 x 62.1 mm (3.78 x 2.4 in)
Compression Ratio 12.5:1
Fuel System Fuel Injection
Starter Primary kick
Lubrication Semi-dry sump
DRIVE TRAIN
Transmission 5-speed constant mesh
Clutch Wet multi-plate type, manual release
Final Drive Chain, DID520MXV4, 114 links
CHASSIS
Suspension Front Inverted telescopic, air spring, oil damped
Suspension Rear Link type, coil spring, oil damped
Brakes Front Disc brake, single rotor
Brakes Rear Disc brake, single rotor
Tires Front 80/100-21 51M, tube type
Tires Rear 110/90-19 62M, tube type
Fuel Tank Capacity 6.2 L (1.6 US gallons)
Color Champion Yellow No.2 / Solid Black
ELECTRICAL
Ignition Electronic Ignition (CDI)
DIMENSIONS
Overall Length 2190 mm (86.2 in)
Overall Width 830 mm (32.7 in)
Overall Height 1270 mm (50.0 in)
Wheelbase 1495 mm (58.9 in)
Ground Clearance 325 mm (12.8 in)
Seat Height 955 mm (37.6 in)
Curb Weight 112 kg (247 lbs)
Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

@@ -0,0 +1,71 @@
package ch.zhaw.prog2.io;
import java.io.*;
import java.util.*;
public class FileCopy {
public static void main(String[] args) throws IOException {
// get the filename from the arguments. By default, use 'files'-directory in current working directory.
String sourceDirPath = args.length >= 1 ? args[0] : "./files";
File sourceDir = new File(sourceDirPath);
// Part a Verify the directory structure
// Implement the method 'verifySourceDir()'
System.out.format("Verifying Source Directory: %s%n", sourceDirPath);
try {
verifySourceDir(sourceDir);
} catch (FileNotFoundException error) {
System.err.format("Directory %s does not comply with predefined structure: %s%n", sourceDir.getPath(), error.getMessage());
System.err.println("Terminating programm!");
System.exit(1);
}
System.out.printf("Source Directory verified successfully.");
// Part b Copy the files byte resp. char wise.
// Implement the method 'verifySourceDir()'
System.out.println("Initiating file copies.");
try {
copyFiles(sourceDir);
} catch (IOException error) {
System.err.format("Error creating file copies: %s%n", error.getMessage());
System.err.println("Terminating programm!");
System.exit(2);
}
System.out.println("Files copied successfully.");
}
/**
* Part a directory structure
*
* Verify the directory structure for correctness.
* Correct means, that the directory exists and beside the two files rmz450.jpg and rmz450-spec.txt does not contain
* any other files or directories.
*
* @param sourceDir File source directory to verify it contains the correct structure
* @throws FileNotFoundException if the source directory or required file are missing
* @throws InvalidObjectException if the source directory contains invalid files or directories.
*/
private static void verifySourceDir(File sourceDir) throws FileNotFoundException, InvalidObjectException {
}
/**
* Teilaufgabe b Kopieren von Dateien
*
* Copies each file of the source directory twice, once character-oriented and once byte-oriented.
* Source and target files should be opened and copied byte by byte respectively char by char.
* The target files should be named, so the type of copy can be identified.
*
* @param sourceDir File representing the source directory containing the files to copy
* @throws IOException if an error is happening while copying the files
*/
private static void copyFiles(File sourceDir) throws IOException {
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Gradle build configuration for specific lab module / exercise
*/
// enabled plugins
plugins {
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
}
// Project/Module information
description = 'Lab05 UnderstandingCharsets'
group = 'ch.zhaw.prog2'
version = '2022.1'
// Dependency configuration
repositories {
mavenCentral()
}
dependencies {
}
// Configuration for Application plugin
application {
// Define the main class for the application.
mainClass = 'ch.zhaw.prog2.io.UnderstandingCharsets'
}
// enable console input when running with gradle
run {
standardInput = System.in
}
// Java plugin configuration
java {
// By default the Java version of the gradle process is used as source/target version.
// This can be overridden, to ensure a specific version. Enable only if required.
// sourceCompatibility = JavaVersion.VERSION_17 // ensure Java source code compatibility
// targetCompatibility = JavaVersion.VERSION_17 // version of the created byte-code
// Java compiler specific options
compileJava {
// source files should be UTF-8 encoded
options.encoding = 'UTF-8'
// for more options see https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html
}
}
@@ -0,0 +1,48 @@
package ch.zhaw.prog2.io;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public class UnderstandingCharsets {
public static void main(String[] args) {
/* Teilaufgabe a
* In der Vorlesung haben Sie gelernt, dass Java-Klassen fuer Unicode entworfen wurden.
* Nun ist Unicode aber nicht der einzige Zeichensatz und Java unterstuetz durchaus Alternativen.
* Welche Zeichensaetze auf einem System konkret unterstuetzt werden haengt von der Konfiguration des Betriebssystems JVM ab.
* Schreiben Sie ein Programm, welches alle Unterstuetzten Zeichensaetze auf der Konsole (System.out) ausgibt,
* zusammen mit dem Standardzeichensatz.
* https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html
*/
// ToDo: Print default character set
// Todo: Print all available character sets
/* Ende Teilaufgabe a */
/* Teilaufgabe b
* Ergänzen Sie die Klasse so, dass sie einzelne Zeichen (also Zeichen für Zeichen) im Standardzeichensatz
* von der Konsole einliest und in zwei Dateien schreibt einmal im Standardzeichensatz und einmal im
* Zeichensatz `US-ASCII`.
* Die Eingabe des Zeichens `q` soll das Program ordentlich beenden.
* Die Dateien sollen `CharSetEvaluation_Default.txt` und `CharSetEvaluation_ASCII.txt` genannt und
* werden entweder erzeugt oder, falls sie bereits existieren, geöffnet und der Inhalt überschrieben.
* Testen Sie Ihr Program mit den folgenden Zeichen: a B c d € f ü _ q
* Öffnen Sie die Textdateien nach Ausführung des Programs mit einem Texteditor und erklären Sie das Ergebnis.
* Öffnen Sie die Dateien anschliessend mit einem HEX-Editor und vergleichen Sie.
*/
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Gradle build configuration for specific lab module / exercise
*/
// enabled plugins
plugins {
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
}
// Project/Module information
description = 'Lab05 FileAttributes'
group = 'ch.zhaw.prog2'
version = '2022.1'
// Dependency configuration
repositories {
mavenCentral()
}
dependencies {
}
// Configuration for Application plugin
application {
// Define the main class for the application.
mainClass = 'ch.zhaw.prog2.io.DirList'
}
// enable console input when running with gradle
run {
standardInput = System.in
}
// Java plugin configuration
java {
// By default the Java version of the gradle process is used as source/target version.
// This can be overridden, to ensure a specific version. Enable only if required.
// sourceCompatibility = JavaVersion.VERSION_17 // ensure Java source code compatibility
// targetCompatibility = JavaVersion.VERSION_17 // version of the created byte-code
// Java compiler specific options
compileJava {
// source files should be UTF-8 encoded
options.encoding = 'UTF-8'
// for more options see https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html
}
}
@@ -0,0 +1,26 @@
package ch.zhaw.prog2.io;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class DirList {
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
String pathName = args.length >= 1 ? args[0] : ".";
File file = new File(pathName);
// Write metadata of given file, resp. all of its files if it is a directory
// Whith each file on one line in the following format.
// - type of file ('d'=directory, 'f'=file)
// - readable 'r', '-' otherwise
// - writable 'w', '-' otherwise
// - executable 'x', '-' otherwise
// - hidden 'h', '-' otherwise
// - modified date in format 'yyyy-MM-dd HH:mm:ss'
// - length in bytes
// - name of the file
}
}
+68
View File
@@ -0,0 +1,68 @@
/*
* Gradle build configuration for specific lab module / exercise
*/
// enabled plugins
plugins {
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
}
// Project/Module information
description = 'Lab05 PictureDB'
group = 'ch.zhaw.prog2'
version = '2022.1'
// Dependency configuration
repositories {
mavenCentral()
}
dependencies {
// Junit 5 dependencies
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.+'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.+'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.+'
}
// Test task configuration
test {
// Use JUnit platform for unit tests
useJUnitPlatform()
// Output results of individual tests
testLogging {
events "PASSED", "SKIPPED", "FAILED"
}
}
// Configuration for Application plugin
application {
// Define the main class for the application.
mainClass = 'ch.zhaw.prog2.io.picturedb.PictureImport'
}
// Run task configuration
run {
// enable console input when running with gradle
standardInput = System.in
// set system property to load log configuration using class (takes precedence; if not set or fails, file is used)
systemProperty 'java.util.logging.config.class', 'ch.zhaw.prog2.io.picturedb.LogConfiguration'
// set system property to load log configuration from properties
systemProperty 'java.util.logging.config.file', 'log.properties'
}
// Java plugin configuration
java {
// By default the Java version of the gradle process is used as source/target version.
// This can be overridden, to ensure a specific version. Enable only if required.
// sourceCompatibility = JavaVersion.VERSION_17 // ensure Java source code compatibility
// targetCompatibility = JavaVersion.VERSION_17 // version of the created byte-code
// Java compiler specific options
compileJava {
// source files should be UTF-8 encoded
options.encoding = 'UTF-8'
// for more options see https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html
}
}
+3
View File
@@ -0,0 +1,3 @@
1;2014-03-17 14:30:05;2.324744;48.864506;Another Monkey;http://www.codemonkey.in/images/Code-Monkey.png
13;2014-04-01 02:17:33;77.598736;12.979842;Bête à coder;http://www2.craven.fr/blojsom/resources/default/codemonkey.jpg
14;2013-05-07 13:45:13;-71.098270;42.302583;Need a coder;http://blog.stackoverflow.com/wp-content/uploads/code_monkey_colour.jpg
1 1 2014-03-17 14:30:05 2.324744 48.864506 Another Monkey http://www.codemonkey.in/images/Code-Monkey.png
2 13 2014-04-01 02:17:33 77.598736 12.979842 Bête à coder http://www2.craven.fr/blojsom/resources/default/codemonkey.jpg
3 14 2013-05-07 13:45:13 -71.098270 42.302583 Need a coder http://blog.stackoverflow.com/wp-content/uploads/code_monkey_colour.jpg
+42
View File
@@ -0,0 +1,42 @@
## Console handler configuration
java.util.logging.ConsoleHandler.level = ALL
## File handler configuration
## see https://docs.oracle.com/en/java/javase/11/docs/api/java.logging/java/util/logging/FileHandler.html
java.util.logging.FileHandler.level = ALL
# %g = generation number, %u = unique number to resolve conflicts
java.util.logging.FileHandler.pattern = log-%g-%u.log
# use SimpleFormatter instead of default XMLFormatter
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.encoding = UTF-8
# max log file size in byte before switching to next generation (=10`kB); 0=unlimited
java.util.logging.FileHandler.limit = 10240
# max number of generations (%g) before overwriting (5 -> 0..4)
java.util.logging.FileHandler.count=5
java.util.logging.FileHandler.append=true
## Configure format of log messages
# arguments see https://docs.oracle.com/en/java/javase/17/docs/api/java.logging/java/util/logging/SimpleFormatter.html
# formats see https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Formatter.html
# format "[date] [level] message exception"
java.util.logging.SimpleFormatter.format = [%1$tF %1$tT %1tZ] [%4$s] %5$s%6$s%n
# format "[date] [level] [logger] message exception"
#java.util.logging.SimpleFormatter.format = [%1$tF %1$tT %1tZ] [%4$-7s] [%3$s] %5$s%6$s%n
# format "[date] [level] [position in source] message exception"
#java.util.logging.SimpleFormatter.format = [%1$tF %1$tT %1tZ] [%4$-7s] [%2$s] %5$s%6$s%n
## configure default log level (for all loggers, if not overwritten below)
.level = INFO
## configure root logger ""
handlers = java.util.logging.ConsoleHandler
level = INFO
## Application specific logger configuration
# loggers starting with "ch.zhaw.prog2.io.picturedb" -> use console and file handler
ch.zhaw.prog2.io.picturedb.handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# do not forward to parent handlers
ch.zhaw.prog2.io.picturedb.useParentHandlers = false
# Set log levels for specific packages/classes
ch.zhaw.prog2.io.picturedb.level = INFO
#ch.zhaw.prog2.io.picturedb.FilePictureDatasource.level = FINER
@@ -0,0 +1,67 @@
package ch.zhaw.prog2.io.picturedb;
import java.util.Collection;
/**
* Generic data source interface to persist items of type T extending {@link Record}
* What kind of persistence media is used, is defined by the concrete implementation.
* e.g. InMemory, Files, Database, ...
* The datatype T to be persisted, must extend {@link Record} which contains the id field to uniquely identify the record.
*
* @param <T extends Record> data type to persist
*/
public interface Datasource<T extends Record> {
/**
* Insert a new record to the data source.
* The id field of the record is ignored, and a new unique id has to be generated, which will be set in the record.
* This id is used to identify the record in the dataset by the other methods (i.e. find, update or delete methods)
*
* @param record of type T to insert into the data set.
*/
void insert(T record);
/**
* Update the content of an existing record in the data set, which is identified by the unique identifier,
* with the new values from the given record object.
* If the identifier can not be found in the data set, an {@link RecordNotFoundException} is thrown.
*
* @param record to be updated in the dataset
* @throws RecordNotFoundException if the record is not existing
*/
void update(T record) throws RecordNotFoundException;
/**
* Deletes the record, identified by the id of the given record from the data set.
* All other fields of the record are ignored.
* If the identifier can not be found in the data set, an {@link RecordNotFoundException} is thrown.
*
* @param record to be deleted
* @throws RecordNotFoundException if the record is not existing
*/
void delete(T record) throws RecordNotFoundException;
/**
* Returns the number of records in the data set
* @return number of records
*/
long count();
/**
* Retrieves an instance of the record identified by the given id.
* If the record can not be found, null is returned.
* (better return type would be an {@link java.util.Optional} which is covered in part Functional Programming)
* An empty result is not an error. Therefore, we do not throw an exception.
*
* @param id of the record to be retrieved
* @return record of type T or null if not found
*/
T findById(long id);
/**
* Retrieves all records of the data set.
* If the dataset is empty an empty collection is returned.
*
* @return collection of all records of the data set
*/
Collection<T> findAll();
}
@@ -0,0 +1,106 @@
package ch.zhaw.prog2.io.picturedb;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* Implements the PictureDatasource Interface storing the data in
* Character Separated Values (CSV) format, where each line consists of a record
* whose fields are separated by the DELIMITER value ";"
* See example file: db/picture-data.csv
*/
public class FilePictureDatasource implements PictureDatasource {
// Charset to use for file encoding.
protected static final Charset CHARSET = StandardCharsets.UTF_8;
// Delimiter to separate record fields on a line
protected static final String DELIMITER = ";";
// Date format to use for date specific record fields
protected static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private final DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
/**
* Creates the FilePictureDatasource object with the given file path as datafile.
* Creates the file if it does not exist.
* Also creates an empty temp file for write operations.
*
* @param filepath of the file to use as database file.
* @throws IOException if accessing or creating the file fails
*/
public FilePictureDatasource(String filepath) throws IOException {
// ToDo: Implement
}
/**
* {@inheritDoc}
*
*/
@Override
public void insert(Picture picture) {
// ToDo: Implement
}
/**
* {@inheritDoc}
*
*/
@Override
public void update(Picture picture) throws RecordNotFoundException {
// ToDo: Implement
}
/**
* {@inheritDoc}
*
*/
@Override
public void delete(Picture picture) throws RecordNotFoundException {
// ToDo: Implement
}
/**
* {@inheritDoc}
*
*/
@Override
public long count() {
// ToDo: Correct Implementation
return 0;
}
/**
* {@inheritDoc}
*
*/
@Override
public Picture findById(long id) {
// ToDo: Correct Implementation
return null;
}
/**
* {@inheritDoc}
*
*/
@Override
public Collection<Picture> findAll() {
// ToDo: Correct Implementation
return null;
}
/**
* {@inheritDoc}
*
*/
@Override
public Collection<Picture> findByPosition(float longitude, float latitude, float deviation) {
// ToDo: Correct Implementation
return null;
}
}
@@ -0,0 +1,70 @@
package ch.zhaw.prog2.io.picturedb;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* Configuration class for Java Logging
* Reads configuration from property file, specified in the following order:
* - System property "java.util.logging.config.file" (also used by LogManager)
* - file "log.properties" in working directory
* - file "log.properties" in project resources
* If this class is specified in system property "java.util.logging.config.class" it
* is loaded automatically at system startup.
* Otherwise, it can also be called at startup.
*/
public class LogConfiguration {
private static final Logger logger = Logger.getLogger(LogConfiguration.class.getCanonicalName());
/*
* Static class configuration.
* Only executed once when class is loaded.
* Load Java logger configuration from config file
*/
static {
Locale.setDefault(Locale.ROOT); // show log messages in english
// get log config file (default uses log.properties in working directory)
String logConfigFile = System.getProperty("java.util.logging.config.file", "log.properties");
Path logConfigPath = Path.of(logConfigFile);
try {
InputStream configFileStream;
if (Files.isReadable(logConfigPath)) {
// if available and readable use specified file
configFileStream = Files.newInputStream(logConfigPath);
} else {
// otherwise use minimal config from resources
logConfigFile="resources:/log.properties";
configFileStream = ClassLoader.getSystemClassLoader().getResourceAsStream("log.properties");
}
if (configFileStream != null) {
LogManager.getLogManager().readConfiguration(configFileStream);
logger.fine("Log configuration read from " + logConfigFile);
} else {
logger.warning("No log configuration found. Using system default settings.");
}
} catch (IOException e) {
logger.log(Level.WARNING, "Error loading log configuration", e);
}
}
public static String getProperty(String name) {
return LogManager.getLogManager().getProperty(name);
}
public static void setLogLevel(Class clazz, Level level) {
Logger.getLogger(clazz.getCanonicalName()).setLevel(level);
}
public static Level getLogLevel(Class clazz) {
return Logger.getLogger(clazz.getCanonicalName()).getLevel();
}
}
@@ -0,0 +1,81 @@
package ch.zhaw.prog2.io.picturedb;
import java.net.URL;
import java.util.Date;
import java.util.Objects;
public class Picture extends Record {
private final URL url;
private final Date date;
private final String title;
private final float longitude;
private final float latitude;
public Picture(URL url, Date date, String title, float longitude, float latitude) {
this.url = url;
this.date = date;
this.title = title;
this.longitude = longitude;
this.latitude = latitude;
}
public Picture(URL url, String title) {
this(url, new Date(), title, 0, 0);
}
protected Picture(long id, URL url, Date date, String title, float longitude, float latitude) {
this(url,date, title, longitude, latitude);
this.id = id;
}
public URL getUrl() {
return url;
}
public Date getDate() {
return date;
}
public String getTitle() {
return title;
}
public float getLongitude() {
return longitude;
}
public float getLatitude() {
return latitude;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Picture picture = (Picture) o;
return Float.compare(picture.longitude, longitude) == 0 &&
Float.compare(picture.latitude, latitude) == 0 &&
url.equals(picture.url) &&
date.equals(picture.date) &&
title.equals(picture.title);
}
@Override
public int hashCode() {
return Objects.hash(url, date, title, longitude, latitude);
}
@Override
public String toString() {
return "Picture{" +
"id=" + id +
", url=" + url +
", date=" + date +
", title='" + title + '\'' +
", longitude=" + longitude +
", latitude=" + latitude +
'}';
}
}
@@ -0,0 +1,23 @@
package ch.zhaw.prog2.io.picturedb;
import java.util.Collection;
/**
* Declaration of a Datasource to store Picture records.
* Beside the default Datasource methods, it declares an additional Picture specific query method.
*/
public interface PictureDatasource extends Datasource<Picture> {
/**
* Retrieves all images close to a a certain position.
* All images with a deviation from the exact coordinates are returned.
* This includes all objects in a square range
* from [longitude - deviation / latitude - deviation]
* to [longitude + deviation / latitude + deviation]
*
* @param longitude longitude coordinate of the center of the square area
* @param latitude latitude coordinate of the center of the square area
* @param deviation deviation from the center of the area in longitude and latitude direction
* @return Collection of all Picture records in the area
*/
Collection<Picture>findByPosition(float longitude, float latitude, float deviation);
}
@@ -0,0 +1,106 @@
package ch.zhaw.prog2.io.picturedb;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.logging.Level;
/* This demo-application reads some picture data from terminal,
* saves it to the datasource, read it from the DB and prints the result
*/
public class PictureImport {
private static final String PICTURE_DB = "db/picture-data.csv";
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
private static final PrintWriter out = new PrintWriter(System.out, true);
public static void main (String[] args) throws IOException {
// initialize logger level
LogConfiguration.setLogLevel(FilePictureDatasource.class, Level.FINE);
// create datasource
PictureDatasource dataSource = new FilePictureDatasource(PICTURE_DB);
// read picture data from the terminal
Picture picture = createPicture();
// save the picture to the data source
dataSource.insert(picture);
// read the picture back from file
Picture readPicture = dataSource.findById(picture.getId());
if (readPicture != null) {
out.println("The following pictures has been saved: ");
out.println(readPicture);
} else {
out.println("Picture with id=" + picture.getId() + " not found.");
}
// read all pictures and list them on the console
Collection<Picture> pictures = dataSource.findAll();
out.println("Pictures:");
for (Picture pict : pictures) {
out.println(pict.toString());
}
}
static Picture createPicture() {
// asks the values for the objects
out.println("** Create a new picture **");
URL url = null;
do {
String urlString = prompt("Picture URL: ");
try {
url = new URL(urlString);
} catch (MalformedURLException e1) {
out.println("Malformed URL: " + e1.getMessage());
}
} while (url == null);
String title = prompt("Picture title: ");
Date date = new Date(); // now
try {
date = dateFormat.parse(prompt("Picture time ("+DATE_FORMAT+") Default = now: "));
} catch (ParseException e) {
out.println("Unknown date format. Using "+ dateFormat.format(date));
}
float longitude = 0.0f;
try {
longitude = Float.parseFloat(prompt("Picture position longitude: "));
} catch (NumberFormatException e) {
out.println("Unknown number format. Using " + longitude);
}
float latitude = 0.0f;
try {
latitude = Float.parseFloat(prompt("Picture position latitude: "));
} catch (NumberFormatException e) {
out.println("Unknown number format. Using " + latitude);
}
return new Picture(url, date, title, longitude, latitude);
}
// prompt function -- to read input string
static String prompt(String prompt) {
try {
Scanner scanner = new Scanner(System.in);
out.print(prompt);
out.flush();
return scanner.nextLine().strip();
} catch (NoSuchElementException e) {
return "";
}
}
}
@@ -0,0 +1,43 @@
package ch.zhaw.prog2.io.picturedb;
/**
* Abstract class to be used as base class for data records, which can stored in a {@link Datasource}
* When a record is created it gets the default id of -1, indicating that it is a new record, which is not stored
* in a DataSource.
*/
public abstract class Record {
/**
* Default id for new records, indicating, that it is not yet stored in a datasource.
*/
private static final long DEFAULT_ID = -1L;
/**
* Identifier of the record.
*/
protected long id = DEFAULT_ID;
/**
* Set the id for this record. This method is used by the specific data set handler when adding the record to a
* {@link Datasource}. It should not be used directly by the user.
* @param id new id of the record, when the record is added to the data source
*/
protected void setId(long id) {
this.id = id;
}
/**
* Returns the identifier of the record
* @return identifier of the record
*/
public long getId() {
return id;
}
/**
* Returns the status of the record.
* @return true, if the record is not stored to the data source, false otherwise
*/
public boolean isNew() {
return id == DEFAULT_ID;
}
}
@@ -0,0 +1,26 @@
package ch.zhaw.prog2.io.picturedb;
/**
* Exception indicating that a record could not be found in a datasource.
* The reason why is given as a text message.
*/
public class RecordNotFoundException extends Exception {
public RecordNotFoundException() {
}
public RecordNotFoundException(String message) {
super(message);
}
public RecordNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public RecordNotFoundException(Throwable cause) {
super(cause);
}
public RecordNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,13 @@
## Minimal log configuration setting decent defaults
## Console handler configuration
java.util.logging.ConsoleHandler.level = ALL
## Configure format of log messages
java.util.logging.SimpleFormatter.format = [%1$tF %1$tT %1tZ] [%4$s] [%3$s] %5$s%6$s%n
## configure default log level (for all loggers, if not overwritten below)
.level = INFO
## configure root logger ""
handlers = java.util.logging.ConsoleHandler
level = INFO
@@ -0,0 +1,279 @@
package ch.zhaw.prog2.io.picturedb;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.stream.Stream;
import static ch.zhaw.prog2.io.picturedb.FilePictureDatasource.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
class FilePictureDatasourceTest {
private static final long EXISTING_ID = 13;
private static final long INEXISTENT_ID = 0;
private final DateFormat df = new SimpleDateFormat(DATE_FORMAT);
private final Random random = new Random(new Date().getTime());
/*
* Static class configuration.
* Only executed once when class is loaded.
*/
static {
// logger configuration
try {
// show log messages in english
Locale.setDefault(Locale.ROOT);
// load default minimal log configuration
LogManager.getLogManager().readConfiguration(ClassLoader.getSystemResourceAsStream("log.properties"));
// set level for class to test to WARNING (minimize log messages)
Logger.getLogger(FilePictureDatasource.class.getCanonicalName()).setLevel(Level.WARNING);
} catch (IOException e) {
System.err.println("Failed to read log configuration: " + e.getMessage());
}
}
Path dbTemplatePath; // path of template database
Path dbPath; // path of temporary test database
PictureDatasource datasource = null; // datasource instance to test
FilePictureDatasourceTest() {
URL dbTemplateUrl = FilePictureDatasourceTest.class.getClassLoader().getResource("db");
Objects.requireNonNull(dbTemplateUrl, "Test database directory not found");
String dbDir = new File(dbTemplateUrl.getPath()).getAbsolutePath(); // required for Windows to remove leading '/'
String dbDirRaw = URLDecoder.decode(dbDir, CHARSET); // replace urlencoded characters, e.g. %20 -> " "
dbTemplatePath = Path.of(dbDirRaw, "test-data-template.csv");
dbPath = Path.of(dbDirRaw, "test-data.csv");
}
@BeforeEach
void setUp() throws IOException {
// initialize test database file
Files.copy(dbTemplatePath, dbPath);
// setup datasource
datasource = new FilePictureDatasource(dbPath.toString());
}
@AfterEach
void tearDown() throws IOException {
// cleanup test database file
Files.deleteIfExists(dbPath);
}
@Test
void insert() {
Picture testPicture = createPicture("http://test.url/hallo.img", "Test picture");
assertEquals(-1, testPicture.getId(), "New picture must have Id -1");
datasource.insert(testPicture);
assertNotEquals(-1, testPicture.getId(), "Insert must set new id to picture");
assertTrue(testPicture.getId() > 14, "Id must be larger than last existing");
assertEquals(15, testPicture.getId(), "Id must be 1 larger than current highest");
try {
String insertedLine = readLineNo(4);
assertNotNull(insertedLine, "Inserted line does not exist");
assertEquals(pictureToCsvLine(testPicture), insertedLine);
} catch (IOException e) {
fail("Failed reading inserted picture record");
}
}
@Test
void insertNull() {
assertThrows(NullPointerException.class, () -> datasource.insert(null));
}
@Test
void delete() {
long pictureIdToDelete = EXISTING_ID;
Picture pictureToDelete = datasource.findById(pictureIdToDelete);
assumeTrue(pictureToDelete != null, "Picture to delete not found");
try {
datasource.delete(pictureToDelete);
} catch (RecordNotFoundException e) {
fail("Failed to delete picture", e);
}
try {
String deletedRecord = readLineWithId(pictureIdToDelete);
assertNull(deletedRecord, "Record still found after delete");
} catch (IOException e) {
fail("Failed to read deleted record");
}
}
@Test
void deleteNull() {
assertThrows(NullPointerException.class, () -> datasource.delete(null));
}
@Test
void deleteInexistent() {
Picture testPicture = createPicture("http://test.url/hallo.img", "Test picture");
testPicture.setId(INEXISTENT_ID);
assertThrows(RecordNotFoundException.class, () -> datasource.delete(testPicture));
}
@Test
void update() {
Picture originalPicture = datasource.findById(EXISTING_ID);
assumeTrue(originalPicture != null);
assumeTrue(originalPicture.id == EXISTING_ID);
Date updatedDate = new Date(originalPicture.getDate().getTime() + 60_000);
URL updatedURL = originalPicture.getUrl();
try {
updatedURL = new URL(originalPicture.getUrl() + "/updated");
} catch (MalformedURLException e) {
fail("Invalid URL format", e);
}
Picture updatedPicture = new Picture(
originalPicture.id,
updatedURL,
updatedDate,
originalPicture.getTitle()+" (updated)",
originalPicture.getLongitude() + 10,
originalPicture.getLatitude() + 10);
try {
datasource.update(updatedPicture);
} catch (RecordNotFoundException e) {
fail("Test record not found for update", e);
}
Picture readUpdatedPicture = datasource.findById(originalPicture.getId());
assertNotNull(readUpdatedPicture, "updated picture not found");
assertEquals(updatedPicture.id, readUpdatedPicture.id);
assertEquals(updatedPicture.getUrl(), readUpdatedPicture.getUrl());
assertEquals(updatedPicture.getDate(), readUpdatedPicture.getDate());
assertEquals(updatedPicture.getTitle(), readUpdatedPicture.getTitle());
assertEquals(updatedPicture.getLatitude(), readUpdatedPicture.getLatitude());
assertEquals(updatedPicture.getLongitude(), readUpdatedPicture.getLongitude());
}
@Test
void updateNull() {
assertThrows(NullPointerException.class, () -> datasource.update(null));
}
@Test
void updateInexistent() {
Picture testPicture = createPicture("http://test.url/hallo.img", "Test picture");
testPicture.setId(INEXISTENT_ID);
assertThrows(RecordNotFoundException.class, () -> datasource.update(testPicture));
}
@Test
void count() {
assertEquals(3, datasource.count(), "Count for initial datasource not correct");
datasource.insert(createPicture("http://test.url/hallo.img", "Test picture"));
assertEquals(4, datasource.count(), "Count for updated datasource not correct");
}
@Test
void findById() {
Picture foundPicture = datasource.findById(EXISTING_ID);
assertNotNull(foundPicture, "Picture not found");
assertEquals("2013-05-07 13:45:13", df.format(foundPicture.getDate()));
assertEquals("http://blog.stackoverflow.com/wp-content/uploads/code_monkey_colour.jpg",
foundPicture.getUrl().toExternalForm());
assertEquals("Need a coder", foundPicture.getTitle());
assertEquals(-71.098270, foundPicture.getLongitude(), 0.00001);
assertEquals(42.302583, foundPicture.getLatitude(), 0.00001);
}
@Test
void findByIdInexistent() {
Picture foundPicture = datasource.findById(INEXISTENT_ID);
assertNull(foundPicture, "Inexistent Id found: " + INEXISTENT_ID);
}
@Test
void findAll() {
Collection<Picture> pictures = datasource.findAll();
assertNotNull(pictures, "Collection of pictures must not be null");
assertEquals(countLines(), pictures.size(), "Number of records does not match number of found pictures");
for (Picture picture : pictures) {
assertNotNull(picture, "Found <null> picture in collection");
}
}
@Test
void findByPosition() {
Collection<Picture> pictures = datasource.findByPosition(-75, 41, 4);
assertNotNull(pictures);
assertEquals(2, pictures.size(), "Not correct amount of items found at position");
pictures = datasource.findByPosition(55, 23, 1);
assertEquals(0, pictures.size(), "Found items not to be found");
}
/*
* Helper methods
*/
private Picture createPicture(String url, String title) {
URL testURL = null;
try {
testURL = new URL(url);
} catch (MalformedURLException e) {
fail("Invalid URL format", e);
}
float longitude = -180 + random.nextFloat() * 360; // range [-180..+180[
float latitude = -90 + random.nextFloat() * 180; // range [-90..+90[
return new Picture(testURL, new Date(), title, longitude, latitude);
}
private String readLineNo(int lineNo) throws IOException {
try (Stream<String> lineStream = Files.lines(dbPath, CHARSET)) {
return lineStream.skip(lineNo-1).findFirst().orElse(null);
}
}
private String readLineWithId(long id) throws IOException {
try (Stream<String> lineStream = Files.lines(dbPath, CHARSET)) {
return lineStream.filter(line -> line.strip().startsWith(id+DELIMITER)).findFirst().orElse(null);
}
}
private long countLines() {
try (Stream<String> lineStream = Files.lines(dbPath, CHARSET)) {
return lineStream.filter(Predicate.not(String::isBlank)).count();
} catch (IOException e) {
fail("Failed to count lines in db file", e);
}
return 0;
}
private String pictureToCsvLine(Picture picture) {
assertNotNull(picture, "Picture must not be null");
return String.join(DELIMITER,
String.valueOf(picture.getId()),
df.format(picture.getDate()),
String.valueOf(picture.getLongitude()),
String.valueOf(picture.getLatitude()),
picture.getTitle(),
picture.getUrl().toExternalForm()
);
}
}
@@ -0,0 +1,3 @@
1;2014-03-17 14:30:05;2.324744;48.864506;Another Monkey;http://www.codemonkey.in/images/Code-Monkey.png
14;2014-04-01 02:17:33;77.598736;40.979842;Bête à coder;http://www2.craven.fr/blojsom/resources/default/codemonkey.jpg
13;2013-05-07 13:45:13;-71.098270;42.302583;Need a coder;http://blog.stackoverflow.com/wp-content/uploads/code_monkey_colour.jpg
1 1 2014-03-17 14:30:05 2.324744 48.864506 Another Monkey http://www.codemonkey.in/images/Code-Monkey.png
2 14 2014-04-01 02:17:33 77.598736 40.979842 Bête à coder http://www2.craven.fr/blojsom/resources/default/codemonkey.jpg
3 13 2013-05-07 13:45:13 -71.098270 42.302583 Need a coder http://blog.stackoverflow.com/wp-content/uploads/code_monkey_colour.jpg