package de.ostfalia.algo.ws18.s1; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.function.Predicate; import de.ostfalia.algo.ws18.base.IManagement; import de.ostfalia.algo.ws18.base.IMember; import de.ostfalia.algo.ws18.base.KindOfSport; public class Management implements IManagement { /** * Track the number of operations. */ private int numberOfOperations = 0; /** * Track the execution time. */ private long lastCheckpointNs; /** * Current head node. */ private LinkedListNode head; /** * Length of the list. */ private int size = 0; /** * Whether the list is reversed, i. e. the head is the tail. */ private boolean reversed = false; /** * Empty constructor. */ public Management() { } /** * Insert from an array of strings. * * @param membersCsv Array of CSV rows. */ public Management(String[] membersCsv) { for (String memberCsv : membersCsv) { this.insert(new Member(memberCsv)); } } /** * Read from the CSV file and insert. * * @param filename Path to the CSV file. */ public Management(String filename) { this.importFromFile(filename); } /** * Read from the CSV file and insert. * * @param filename Path to the CSV file. */ public void importFromFile(String filename) { File file = new File(filename); try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) { bufferedReader.lines().forEach(line -> insert(new Member(line))); } catch (IOException exception) { throw new IllegalArgumentException(exception); } } /** * Create a filtered version of this list. * * @param criteria Criteria to filter for. * @param breakAfterFirstMatch If true, return after the first match. * @return A new list where all elements meet the given criteria. */ private Management filter(Predicate criteria, boolean breakAfterFirstMatch) { Management result = new Management(); if (this.size == 0) { return result; } // searches from head, inserts at head -> result is reversed result.reverse(); LinkedListNode currentNode = this.head; do { // traverse = 1 operation this.numberOfOperations++; IMember currentValue = currentNode.getValue(); if (criteria.test(currentValue)) { result.insert(currentValue); if (breakAfterFirstMatch) { break; } } } while ((currentNode = currentNode.getNext()) != null); return result; } /** * Find the first element that matches the given criteria. * * @param criteria Criteria to search for. * @return The first element that matches the given criteria or null. */ private IMember search(Predicate criteria) { Management filteredList = this.filter(criteria, true); if (filteredList.size() == 0) { return null; } else { return filteredList.head.getValue(); } } /** * Count the number of elements that match the given criteria. * * @param criteria Criteria to filter for. * @return The number of elements that match the given criteria. */ private long size(Predicate criteria) { return this.filter(criteria, false).size; } /** * Reverse the order of the list. */ public void reverse() { this.reversed = !this.reversed; } /** * @return The length of this list. */ @Override public int size() { return this.size; } /** * Add an element at the start of the list. * Replace the current head with a new node and link this node to the old head. * * @return TODO? */ @Override public boolean insert(IMember value) { this.numberOfOperations++; LinkedListNode newHead = new LinkedListNode(value); if (this.head != null) { newHead.setNext(this.head); } this.head = newHead; this.size++; return true; // TODO? } /** * Find the first member with the given key. * * @param key The key to search for. * @return The member or null. */ @Override public IMember search(long key) { return this.search(member -> member.getKey() == key); } /** * Find the first member with the given name. * * @param name The name of the member. * @param firstName first name of the member. * @return The member or null. */ @Override public IMember search(String name, String firstName) { return this.search(member -> member.getName().equals(name) && member.getFirstName().equals(firstName)); } /** * Count the number of members which have an association to the given kindOfSport. * * @param kindOfSport The kindOfSport of the members. * @return The number of members with the given kindOfSport. */ @Override public int size(KindOfSport kindOfSport) { return (int) this.size(member -> member.getKindOfSport().equals(kindOfSport)); } /** * Return the members which have an association to the given kindOfSport. * * @param kindOfSport The kindOfSport of the members. * @return An array of the members with the given kindOfSport. */ @Override public IMember[] discipline(KindOfSport kindOfSport) { return this.filter(member -> member.getKindOfSport().equals(kindOfSport), false) .toArray(); } /** * Convert the list to an array. * * @return The elements of this list as array. */ @Override public IMember[] toArray() { IMember[] array = new IMember[this.size]; if (this.size() == 0) { return array; } LinkedListNode currentNode = this.head; for (int index = 0; index < this.size; index++) { if (reversed) { array[this.size - index - 1] = currentNode.getValue(); } else { array[index] = currentNode.getValue(); } currentNode = currentNode.getNext(); } return array; } /** * Reset the number of operation counter and get the last value. * * @return The number of operations recorded since the last call. */ @Override public int numberOfOperations() { int numberOfOperations = this.numberOfOperations; this.numberOfOperations = 0; return numberOfOperations; } /** * Reset the timer and get the time difference in ns. * * @return Nanoseconds since the last call. */ public long checkpoint() { long nanoseconds = this.lastCheckpointNs; this.lastCheckpointNs = System.nanoTime(); return this.lastCheckpointNs - nanoseconds; } }