Skip to content
Snippets Groups Projects
Commit b8435af2 authored by Dao's avatar Dao
Browse files

feat(part4): Exercise 4.

parent 4bb970df
No related branches found
No related tags found
No related merge requests found
/.idea
/out
/exercise_4.iml
\ No newline at end of file
package fi.utu.tech.ooj.exercise4;
import fi.utu.tech.ooj.exercise4.exercise4.Exercise4;
public class Main {
public static void main(String[] args) throws Exception {
System.out.println("Advanced Course in Object-Oriented Programming, Part 4 Exercises");
new Exercise4();
}
}
File added
package fi.utu.tech.ooj.exercise4.exercise4;
import java.io.IOException;
public class Exercise4 {
public Exercise4() {
System.out.println("Exercise 4");
System.out.println("Exercise 1");
try (var zipper = new TestZipper("books.zip")) {
zipper.run();
} catch (IOException e) {
System.err.println("Execution failed!");
e.printStackTrace();
}
System.out.println();
System.out.println("-----------------------------------------");
System.out.println("Exercise 2");
try (var zipper = new TestZipper2("books.zip")) {
zipper.run();
} catch (IOException e) {
System.err.println("Execution failed!");
e.printStackTrace();
}
}
}
package fi.utu.tech.ooj.exercise4.exercise4;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.regex.Pattern;
/**
* Test zipper.
* <p>
* Extracts zip, iterates through the files and prints information from each file.
* <p>
* From each file there will be printed:
* - name
* - amount of lines
* - amount of words
*/
class TestZipper extends Zipper<Void> {
TestZipper(String zipFile) throws IOException {
super(zipFile);
}
@Override
protected Handler<Void> createHandler(Path file) {
return new Handler<>(file) {
@Override
public Void handle() throws IOException {
var regex = Pattern.compile("\\W");
var contents = Files.readString(file);
var lines = Files.readAllLines(file);
var firstLine = lines.isEmpty() ? "unknown" : lines.getFirst();
var words = regex.splitAsStream(contents).filter(s -> !s.isBlank()).map(String::toLowerCase).toList();
System.out.printf("""
Originally was fetched from %s.
The founded file is %s.
The file contains %d lines.
The file contains %d words.
Possible title of the work: %s
""",
tempDirectory,
file.getFileName(),
lines.size(),
words.size(),
firstLine
);
return null;
}
};
}
}
package fi.utu.tech.ooj.exercise4.exercise4;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toCollection;
class Book implements Comparable<Book> {
final Path file;
final String title;
final int numberOfLines;
List<String> words;
List<String> uniqueWords;
public Book(Path file, String title, int numberOfLines) {
Objects.requireNonNull(file);
Objects.requireNonNull(title);
this.file = file;
this.title = title;
this.numberOfLines = numberOfLines;
}
@Override
public int compareTo(Book book) {
return title.compareTo(book.getTitle());
}
@Override
public String toString() {
return "File: " + file
+ ". Title:" + title
+ ". Number of lines: " + numberOfLines;
}
public List<String> words() throws IOException {
if (words == null) {
var regex = Pattern.compile("\\W");
var contents = Files.readString(file);
words = regex.splitAsStream(contents).filter(s -> !s.isBlank()).map(String::toLowerCase).toList();
}
return words;
}
public List<String> uniqueWords() throws IOException {
if (uniqueWords == null) {
List<String> words = words();
uniqueWords = words.stream()
.distinct()
.toList();
}
return uniqueWords;
}
public String getTitle() {
return title;
}
public int getNumberOfLines() {
return numberOfLines;
}
}
interface BookSorter<B extends Book> {
<T extends B> List<T> sortByTitleAsc(List<T> books);
<T extends B> List<T> sortByNumberOfLineAsc(List<T> books);
<T extends B> List<T> sortByNumberOfUniqueWordsDesc(List<T> books);
<T extends B> List<T> sortByTitleAscThenNumberOfUniqueWordsDesc(List<T> books);
}
class MutableBookSorter<B extends Book> implements BookSorter<B> {
private final Comparator<B> naturalComparator;
private final Comparator<B> lineCountComparator;
private final Comparator<B> uniqueWordsComparator;
public MutableBookSorter() {
naturalComparator = Comparator.naturalOrder();
lineCountComparator = Comparator.comparing(B::getNumberOfLines);
uniqueWordsComparator = (book1, book2) -> {
int uniqueWordsCount1, uniqueWordsCount2;
try {
uniqueWordsCount1 = book1.uniqueWords().size();
uniqueWordsCount2 = book2.uniqueWords().size();
} catch (IOException e) {
throw new RuntimeException(e);
}
return uniqueWordsCount2 - uniqueWordsCount1;
};
}
@Override
public <T extends B> List<T> sortByTitleAsc(List<T> books) {
books.sort(naturalComparator);
return books;
}
@Override
public <T extends B> List<T> sortByNumberOfLineAsc(List<T> books) {
books.sort(lineCountComparator);
return books;
}
@Override
public <T extends B> List<T> sortByNumberOfUniqueWordsDesc(List<T> books) {
books.sort(uniqueWordsComparator);
return books;
}
@Override
public <T extends B> List<T> sortByTitleAscThenNumberOfUniqueWordsDesc(List<T> books) {
books.sort(naturalComparator.thenComparing(uniqueWordsComparator));
return books;
}
}
class ImmutableBookSorter<B extends Book> extends MutableBookSorter<B> {
@Override
public <T extends B> List<T> sortByTitleAsc(List<T> books) {
var copiedBooks = copy(books);
return super.sortByTitleAsc(copiedBooks);
}
@Override
public <T extends B> List<T> sortByNumberOfLineAsc(List<T> books) {
var copiedBooks = copy(books);
return super.sortByNumberOfLineAsc(copiedBooks);
}
@Override
public <T extends B> List<T> sortByNumberOfUniqueWordsDesc(List<T> books) {
var copiedBooks = copy(books);
return super.sortByNumberOfUniqueWordsDesc(copiedBooks);
}
@Override
public <T extends B> List<T> sortByTitleAscThenNumberOfUniqueWordsDesc(List<T> books) {
var copiedBooks = copy(books);
return super.sortByTitleAscThenNumberOfUniqueWordsDesc(copiedBooks);
}
private <T extends B> List<T> copy(List<T> books) {
return new ArrayList<>(books);
}
}
class TestZipper2 extends Zipper<Book> {
TestZipper2(String zipFile) throws IOException {
super(zipFile);
}
@Override
protected Handler<Book> createHandler(Path file) {
return new Handler<>(file) {
@Override
public Book handle() throws IOException {
var lines = Files.readAllLines(file);
var firstLine = lines.isEmpty() ? "unknown" : lines.getFirst();
return new Book(file, firstLine, lines.size());
}
};
}
@Override
protected FileObjectsHandler<Book> createFileObjectsHandler() {
return books -> {
List<Book> sortedBooks;
BookSorter<Book> sorter = new ImmutableBookSorter<>();
Consumer<Book> printBookInformation = (Book book) -> {
System.out.println("Book: " + book);
try {
List<String> uniqueWords = book.uniqueWords();
System.out.println("Unique words: " + uniqueWords);
System.out.println("Unique words count: " + uniqueWords.size());
System.out.println();
} catch (IOException e) {
throw new RuntimeException(e);
}
};
System.out.println();
System.out.println("Original books:");
books.forEach(System.out::println);
System.out.printf("""
Handled %d Books.
Now we could sort it out a bit.
""", books.size());
System.out.println("1. Sort by title (asc):");
sortedBooks = sorter.sortByTitleAsc(books);
sortedBooks.forEach(System.out::println);
System.out.println();
System.out.println("2. Sort by number of lines (asc):");
sortedBooks = sorter.sortByNumberOfLineAsc(books);
sortedBooks.forEach(System.out::println);
System.out.println();
System.out.println("3. Sort by number of unique words (desc):");
sortedBooks = sorter.sortByNumberOfUniqueWordsDesc(books);
sortedBooks.forEach(printBookInformation);
System.out.println();
System.out.println("4. Sort by title (asc) then number of unique words (desc):");
sortedBooks = sorter.sortByTitleAscThenNumberOfUniqueWordsDesc(books);
sortedBooks.forEach(printBookInformation);
System.out.println();
System.out.println("Original books:");
books.forEach(System.out::println);
};
}
}
package fi.utu.tech.ooj.exercise4.exercise4;
import fi.utu.tech.ooj.exercise4.Main;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.zip.ZipInputStream;
// WORKAROUND: if the zip file is not found, copy books.zip from the resources directory
// to the project's root and follow the two instructions below,
// also marked with WORKAROUND comments.
/**
A class that models unzipping (extracting a compressed zip package).
<p>
The idea is that while an object of the class exists, there is also a temporary directory
created by the object on the disk. When the object is closed, the directory is also deleted.
<p>
How to use it? Create an object. Creation assumes that the zip file must exist.
The class's 'run' method activates the unzipping. Finally, close the object ('close').
<p>
Hint: closing is easy with Java's try-with-resources feature.
*/
abstract public class Zipper<E> implements AutoCloseable {
// zip-file for unzipping
private final String zipFile;
// java class, from which package the zip file is looked for
private final Class<?> resolver = Main.class;
// path of temp directory
protected final Path tempDirectory;
private final List<E> objects;
/**
* Records the given zip file and
* creates a temporary directory 'tempDirectory'.
*
* @param zipFile Zip file path (precondition: must exist and be non-null).
* @throws IOException If the zip file is not found or the temporary directory cannot be created.
*/
public Zipper(String zipFile) throws IOException {
// WORKAROUND: if the zip file is not found, comment out the next two lines.
if (resolver.getResource(zipFile) == null)
throw new FileNotFoundException(zipFile);
this.zipFile = zipFile;
this.objects = new ArrayList<>();
tempDirectory = Files.createTempDirectory("dtek0066");
System.out.println("Created a temp directory " + tempDirectory);
}
/**
* Deletes the temporary directory 'tempDirectory' when the object is closed.
*
* @throws IOException In case of any I/O errors.
*/
@Override
public void close() throws IOException {
try (final var stream = Files.walk(tempDirectory)) {
stream
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
System.out.println("The removed temp directory was " + tempDirectory);
}
/**
* Unzip the file 'zipFile' to the temporary directory 'tempDirectory'.
*
* @throws IOException In case of any I/O errors.
*/
private void unzip() throws IOException {
final var destinationDir = tempDirectory.toFile();
// WORKAROUND: If the zip file is not found, change to the following
// try (final var inputStream = new FileInputStream(zipFile);
try (final var inputStream = resolver.getResourceAsStream(zipFile);
final var stream = new ZipInputStream(inputStream)) {
var zipEntry = stream.getNextEntry();
while (zipEntry != null) {
final var newFile = new File(destinationDir, zipEntry.getName());
final var destDirPath = destinationDir.getCanonicalPath();
final var destFilePath = newFile.getCanonicalPath();
if (!destFilePath.startsWith(destDirPath + File.separator)) {
throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
}
System.out.println("Puretaan " + newFile);
if (zipEntry.isDirectory()) {
if (!newFile.isDirectory() && !newFile.mkdirs()) {
throw new IOException("Failed to create directory: " + newFile);
}
} else {
// fix for Windows-created archives
final var parent = newFile.getParentFile();
if (!parent.isDirectory() && !parent.mkdirs()) {
throw new IOException("Failed to create directory: " + parent);
}
// write file content
try (final var fos = new FileOutputStream(newFile)) {
stream.transferTo(fos);
}
}
zipEntry = stream.getNextEntry();
}
stream.closeEntry();
}
}
/**
* Executes unzipping and creates a handler for every created file.
*
* @throws IOException In case of any I/O errors.
*/
public void run() throws IOException {
unzip();
for (final var handler : createHandlers()) {
E object = handler.handle();
if (object != null) {
objects.add(object);
}
}
var fileObjectsHandler = createFileObjectsHandler();
fileObjectsHandler.handle(objects);
}
protected List<Handler<E>> createHandlers() throws IOException {
try (final var stream = Files.list(tempDirectory)) {
return stream.map(this::createHandler).toList();
}
}
/**
* Creation of the Handler.
*
* @param file The file to be handerl (precondition: must exist and be non-null)
* @return Handler
*/
protected abstract Handler<E> createHandler(Path file);
protected FileObjectsHandler<E> createFileObjectsHandler() {
return fileObjects -> {
};
}
/**
* A handler for a single file, responsible for processing
* an individual file.
*/
protected abstract static class Handler<E> {
public final Path file;
/**
* Initializes the handler.
*
* @param file The file to be handled (precondition: must exist and be non-null)
*/
public Handler(Path file) {
this.file = file;
}
/**
* Processes the file.
*
* @throws IOException In case of any I/O errors.
*/
abstract public E handle() throws IOException;
}
protected interface FileObjectsHandler<E> {
void handle(List<E> fileObjects);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment