· 8 years ago · Feb 13, 2018, 02:08 PM
1PABS
2Courses
3Programmierpraktikum
4WueTunes
5Richard Wiebe (s364302) (Logout)
6WueTunes
7Ziel der Aufgabe
8Ziel dieser Aufgabe ist es, eine Anwendung zum Einlesen, Verwalten und Abspielen von MP3-Dateien zu erstellen. Von zentraler Bedeutung sind dabei die Metadaten ("Daten über Daten") eines Musikstücks, die als sogenannter ID3Tag in der Datei selbst gespeichert sind. Diese Metadaten sollen aus Dateien ausgelesen und in eine Datenbank geschrieben werden.
9Die Aufgabe gliedert sich dabei wie folgt:
10
11Teilaufgabe 0: Datenvalidierung (2 Punkte)
12Teilaufgabe 1: Verwaltung von Musikstücken (15 Punkte)
13Teilaufgabe 2: Auslesen, Verwalten und Konvertieren von ID3Tags aus MP3-Dateien (30 Punkte)
14Teilaufgabe 3: Schreiben bzw. Lesen von Musikstücken in eine bzw. von einer SQLite Datenbank (15 Punkte)
15Teilaufgabe 4: Die JavaFX-Anwendung WueTunes (19 Punkte)
16Teilaufgabe 5: Berechnung von Ähnlichkeiten von Musikstücken (19 Punkte) (separate Aufgabe in PABS)
17Die Angaben zu den Punkten sind vorläufig uns als grobe Orientierung zu verstehen.
18
19Allgemeine Hinweise
20Verwenden Sie unter keinen Umständen System.exit()! Dies beendet nicht nur Ihr Programm, sondern auch die Testumgebung des PABS-Servers. Lösungen, die System.exit() verwenden, können nicht abgegeben werden und werden mit 0 Punkten bewertet.
21Verwenden Sie nie statische Felder, die nicht final sind!
22Erstellen Sie alle Ihre Code-Dateien mit UTF-8 Codierung.
23Zeilenumbrüche sind generell mit "\n" oder println() zu erzeugen.
24Beachten Sie, dass zur Zeit aus technischen Gründen leider nur das Java Feature-Level 1.8 unterstützt wird. Funktionen aus Java 9 können nicht verwendet werden.
25Lesen Sie in der Java API Dokumentation nach! Nicht nur, wenn das in der Aufgabenstellung explizit gefordert wird, sondern immer, wenn Sie eine Klasse verwenden, die Sie nicht auswendig kennen.
26Eine von PABS als falsch erkannte Abgabe ist falsch. Die Umkehrung gilt nicht!
27Jede testbare Abgabe wird vom PABS System als "accepted" gekennzeichnet werden. Dies bedeutet nicht, dass Sie volle Punktzahl (oder überhaupt Punkte) auf diese Abgabe bekommen. Ausschlaggebend sind ausschließlich Art und Anzahl bestandener Tests, sowie eventuell weiterer unsichtbarer Tests.
28Testen Sie ihre Klassen bzw. deren Methoden jeweils selbst, z. B. indem Sie eine Test-Klasse mit einer main Methode implementieren, in der Sie die zu testende Methode mit verschiedenen Argumenten aufrufen und die Ergebnisse prüfen.
29Für jede Methode bzw. für jeden Konstruktor gilt zusätzlich, dass eine java.lang.IllegalArgumentException geworfen soll, falls mindestens einer der übergebenen Parameter null ist.
30In den PABS-Tests für diese Aufgabe werden sogenannte Mock-Objekte verwendet. Dabei handelt es sich um Java-Objekte, die das Verhalten von echten Objekten einer Klasse simulieren. Dies führt dazu, dass Tests einer Klasse A auch dann erfolgreich sein können, wenn eine dabei verwendete Klasse B nicht korrekt implementiert ist. Es ist dazu allerdings erforderlich, dass Klasse B mit allen geforderten Methoden implementiert ist und kompiliert.
31Es ist ausdrücklich erwünscht, weitere private Methoden zur besseren Strukturierung und Übersichtlichkeit des Quellcodes zu implementieren. Es ist nicht gestattet, weitere nach außen sichtbare Objektmethoden zu implementieren!
32Viel Spaß und viel Erfolg!
33Teilaufgabe 0: Hilfsklassen
340.1 Datenvalidierung
350.1.1 Die Klasse Validate
36Erstellen Sie die Klasse im Paket jpp.wuetunes.util.
37In dieser Klasse implementieren Sie einige statische Methoden zur Validierung von Daten.
38Verwenden Sie diese Methoden in weiteren Aufgabenteilen insbesondere dazu, um die einer Methode als Argumente übergebenen Objekte bzw. Datenwerte zu validieren.
39Sofern die Bedingungen erfüllt sind, geben die Methoden jeweils das übergebene Objekt bzw. den übergebenen Datenwert unverändert zurück, analog zur Funktionsweise der Methode Objects.requireNonNull aus der Java API.
40Erfüllt das übergebene Objekt bzw. der übergebene Datenwert die Bedingungen nicht, wird eine java.lang.IllegalArgumentException geworfen.
41
42public static int requireNonNegative(int n)
43Überprüft, ob die übergebene Zahl nicht negativ ist.
44public static int requireBetween(int n, int min, int max)
45Überprüft, ob die übergebene Zahl innerhalb der übergebenen Grenzen (inklusive) liegt.
46public static int requireLowerThan(int n, int max)
47Überprüft, ob die übergebene Zahl echt kleiner als das übergebene Maximum ist.
48public static <O> O requireNonNull(O o)
49Überprüft, ob das übergebene Objekt nicht null ist.
50Lesen Sie ggf. die Dokumentation zu generische Methoden.
51public static String requireNonNullNotEmpty(String s)
52Überprüft, ob die übergebene Zeichenkette nicht null und nicht leer ist.
53public static String requireLength(String s, int length)
54Überprüft, ob die übergebene Zeichenkette nicht null und nicht leer ist und die übergebene Länge hat.
55public static Path requireFileExists(Path path)
56Überprüft, ob der übergebene Pfad nicht null ist und ob eine durch den übergenenen Pfad referenzierte reguläre (also z. B. kein Verzeichnis) Datei existiert .
57public static byte[] requireNonNullNotEmpty(byte[] bytes)
58Überprüft, ob der übergebene Array nicht null und nicht leer ist.
59public static byte[] requireLength(byte[] bytes, int length)
60Überprüft, ob der übergebene Array nicht null und nicht leer ist und die übergebene Länge hat.
61public static <C extends Collection> C requireNonNullNotEmpty(C c)
62Überprüft, ob die übergebene Collection nicht null und nicht leer ist.
63public static String requireValidImageMimeType(String mimeType)
64Überprüft, ob die übergebene Zeichenkette ein valider MIME-Typ des Content Types image ist. Dies ist der Fall, wenn die Zeichenkette die Form image/<subtype> haben, wobei <subtype> für eine Zeichenkette von mindestens 1 und höchstens 127 Zeichen steht, in der nur die Zeichen a-z, 0-9, "-" (Minus) oder "." (Punkt) vorkommen können. Weitere zu akzeptierende Beispiele finden Sie hier.
65Tipp: Verwenden Sie zur Validierung einen regulären Ausdruck.
66Teilaufgabe 1: Basisklassen
67In dieser Teilaufgabe erstellen Sie Basisklassen zur Verwaltung einer Sammlung von Musikstücken.
68Die Implementierung dieser Klassen ist Voraussetzung für die Implementierung der weiteren Teilaufgaben.
69
701.1 Metadaten
71Die folgenden Klassen modellieren die Metadaten eines Musikstücks.
72Alle folgenden Klasse sollen im im Paket jpp.wuetunes.model.metadata erstellt werden.
73
741.1.1 Die Klasse Genre
75Die Klasse modelliert das Genre eines Musikstücks.
76Die Klasse soll das Interface Comparable<Genre> direkt implementieren, das nach der ID des Genres vergleichen soll.
77Erstellen Sie in der Klasse mindestens den folgenden Konstruktor und die folgenden Methoden:
78
79public Genre(int id, String name)
80Erzeugt eine Instanz mit der ID id und dem Namen name. Ist die übergebene ID negativ oder der übergebene Name leer soll eine java.lang.IllegalArgumentException geworfen
81werden.
82public String getName()
83Gibt den Namen des Genres zurück.
84public int getId()
85Gibt die ID des Genres zurück.
86public String toString()
87Gibt die Zeichenkette <id>. <name> zurück, also z. B. "8. Jazz".
88Überschreiben Sie die Methoden public boolean equals(Object obj) und public int hashCode() so, dass zwei Genres genau dann gleich sind, wenn deren IDs gleich sind.
89
901.1.2 Die Klasse GenreManager
91Diese Klasse verwaltet Genres. Eine Instanz dient dazu, sich die bekannten Genres zu merken und neuen Genres, bei denen nur der Name bekannt ist, die nächste freie ID zuzuweisen.
92Er stellt außerdem sicher, dass es keine Genres mit der gleichen ID oder dem gleichen Namen gibt.
93Erstellen Sie in der Klasse mindestens die folgenden Konstruktoren und Methoden:
94
95public GenreManager()
96Erzeugt einen GenreManager, dem noch keine Genres bekannt sind.
97public GenreManager(Collection<Genre> genres)
98Erzeugt einen GenreManager, dem die übergebenen Genres bekannt sind.
99Befinden sich in der übergebenen Collection mehrere Genres mit der gleichen ID oder dem gleichen Namen oder kennt der GenreManager bereits Genres mit der ID oder dem Namen eines übergebenen Genres, soll eine java.lang.IllegalArgumentException geworfen werden.
100Die übergebene Collection darf (auch bei Aufrufen weiterer Methoden dieser Klasse) nicht verändert werden.
101public Genre add(Genre genre)
102Fügt dem GenreManager ein Genre hinzu und gibt es zurück.
103Kennt der GenreManager bereits Genres mit der ID oder dem Namen des übergebenen Genres, soll eine java.lang.IllegalArgumentException geworfen werden.
104public Genre add(String name)
105Erzeugt ein neues Genre mit dem übergebenen Namen und einer neuen ID und gibt es zurück. Die neue ID soll dabei die "nächste freie ID" sein, also die kleinste nicht negative Zahl für die der GenreManager noch kein Genre kennt.
106Ist dem GenreManager bereits ein Genre mit dem gleichen Namen bekannt, wird kein neues Genre hinzugefügt, sondern dieses zurückgegeben.
107public Optional<Genre> getGenreById(int id)
108Gibt ein Optional<T> eines Genres zurück. Kennt der GenreManager ein Genre mit der übergebenen ID, soll das Optional dieses Genre enthalten. Andernfalls soll das Optional leer sein.
109Ist id negativ, soll eine java.lang.IllegalArgumentException geworfen werden.
110public Optional<Genre> getGenreByName(String name)
111Gibt ein Optional<T> eines Genres zurück. Kennt der GenreManager ein Genre mit dem übergebenen Namen, soll das Optional dieses Genre enthalten. Andernfalls soll das Optional leer sein.
112Ist name leer, soll eine java.lang.IllegalArgumentException geworfen werden.
113public List<Genre> getGenres()
114Gibt eine Liste der dem GenreManager bekannten Genres zurück, die aufsteigend nach der ID der Genres sortiert ist.
115Kennt der GenreManager keine Genres, soll eine leere Liste zurückgegeben werden.
116Nachfolgende Änderungen an der zurückgegebenen Liste dürfen den den Zustand des GenreManager nicht verändern.
1171.1.3 Die Klasse MetadataPicture
118Die Klasse modelliert ein Bild in den Metadaten eines Musikstücks.
119Erstellen Sie in der Klasse mindestens den folgenden Konstruktor und die folgenden Methoden:
120
121public MetadataPicture(String mimeType, String description, byte[] data)
122Erzeugt eine Instanz mit dem übergebenen MIME-Typ, der übergebenen Bildbeschreibung und den übergebenen rohen Bilddaten.
123Der MIME-Typ muss die Form eines gültigen MIME-Typs haben. (siehe Teilaufgabe 0 die Methode requireValidImageMimeType(String mimeType) der Klasse Validate)
124Hat die Zeichenkette eine andere Form, soll eine java.lang.IllegalArgumentException geworfen werden.
125Haben die Daten eine Länge von 0, soll eine java.lang.IllegalArgumentException geworfen werden.
126public String getMimeType()
127Gibt den MIME Type zurück.
128public String getDescription()
129Gibt die Bildbeschreibung zurück.
130public byte[] getData()
131Gibt die Bilddaten zurück.
132public String toString()
133Gib die Zeichenkette MIME type: "<MIME Type>", "<Bildbeschreibung>", <Länge der Bilddaten> bytes zurück
134Beispiel: MIME type: "image/jpg", "Front Cover", 12345 bytes
135Überschreiben Sie die Methoden public boolean equals(Object obj) und public int hashCode() so, dass zwei Bilder genau dann gleich sind, wenn deren MIME Type, die Beschreibung und die Rohdaten gleich sind.
136Eine Änderung der im Konstruktor übergebenen Bilddaten (außerhalb dieser Klasse) darf den Zustand der Instanz dieser Klasse nicht verändern.
137Eine Änderung der mit der Methode getData zurückgegebenen Bilddaten (außerhalb dieser Klasse) darf den Zustand der Instanz dieser Klasse nicht verändern.
138
1391.1.4 Die Klasse Metadata
140Diese Klasse modelliert die gesamten Metadaten für ein Musikstück.
141Die get-Methoden dieser Klasse liefern Objekte vom Typ Optional<T> zurück (siehe auch entsprechendes Kapitel im Javabuch). Ist die entsprechende Information nicht vorhanden, soll ein leeres Optional zurückgegeben werden.
142Erstellen Sie in der Klasse mindestens die folgenden Methoden:
143
144public void setSongTitle(String songTitle)
145Setzt den Titel des Musikstücks.
146public Optional<String> getSongTitle()
147Gibt den Titel zurück.
148public void setArtist(String artist)
149Setzt den Künstler des Musikstücks.
150public Optional<String> getArtist()
151Gibt den Künstler zurück.
152public void setAlbumTitle(String albumTitle)
153Setzt den Titel des Albums des Musikstücks.
154public Optional<String> getAlbumTitle()
155Gibt den Titel des Albums zurück.
156public void setTrackNumber(int trackNumber)
157Setzt die Nummer des Musikstücks auf dem Album. Ist der übergebene Wert negativ, soll eine java.lang.IllegalArgumentException geworfen werden.
158public Optional<Integer> getTrackNumber()
159Gibt die Nummer des Titels auf dem Album zurück.
160public void setGenre(Genre genre)
161Setzt das Genre eines Musikstücks.
162public Optional<Genre> getGenre()
163Gibt das Genre zurück.
164public void setDate(Temporal date)
165Setzt einen Datumswert, der angibt, wann das Musikstück aufgenommen wurde.
166(Das übergebene Objekt ist im Rahmen dieser Aufgabe steht eine Instanz von LocalDateTime, LocalDate, YearMonth oder Year aus dem Paket java.time. Dies muss allerdings nicht überprüft werden.)
167public Optional<Temporal> getDate()
168Gibt den entsprechenden Datumswert zurück.
169public void setPicture(MetadataPicture picture)
170Setzt ein Bild für das Musikstück.
171public Optional<MetadataPicture> getPicture()
172Gibt das entsprechende Bild zurück.
173public void setCopyrightInformation(URL copyrightInformation)
174Setzt eine java.net.URL, unter der Informationen zum Copyright zu finden sind.
175public Optional<URL> getCopyrightInformation()
176Gibt die entsprechende URL zurück.
177public void setPublisherWebpage(URL publisherWebpage)
178Setzt eine java.net.URL für den Herausgeber des Musikstücks.
179public Optional<URL> getPublisherWebpage()
180Gibt die entsprechende URL zurück.
181public void setRating(int rating)
182Setzt die Bewertung des Musikstücks (0 = unbekannt, 1 = schlecheste Bewertung, 5 = beste Bewertung). Ist die übergebene Zahl kleiner als 0 oder größer als 5, soll eine java.lang.IllegalArgumentException geworfen werden.
183public Optional<Integer> getRating()
184Gibt die Bewertung zurück.
185public void setPlayCounter(int playCounter)
186Setzt einen Wiedergabezähler, der angibt, wie oft die Wiedergabe eines Musikstücks bereits gestartet wurde. Ist der übergebene Wert negativ, soll eine java.lang.IllegalArgumentException geworfen werden.
187public Optional<Integer> getPlayCounter()
188Gibt den Wert des Wiedergabezählers zurück.
189public String toString()
190Gibt eine String Repräsentation der Metadaten in folgender Form zurück:
191
192Song title: <Titel des Musikstücks>
193Artist: <Künstler>
194Album title: <Titel des Albums>
195Track number: <Nummer auf dem Album>
196Genre: <String-Repräsentation des Genres>
197Date: <das Jahr des Aufnahmedatums (z. B. "2017")>
198Picture: <String-Repräsentation des Bildes>
199Copyright information: <URL mit Copyright-Informationen>
200Publisher webpage: <URL für den Herausgeber>
201Rating: <r *-Zeichen , wobeir r für den Wert der Bewertung steht>
202Play counter: <Wert des Play counters>
203
204Als Zeilentrennzeichen soll dabei \n verwendet werden.
205Die Ausgabe der einzelnen Zeilen ist dabei optional: Falls ein Metadatum nicht hinterlegt ist, soll die entsprechende Zeile nicht ausgegeben werden.
206
207Beispiel:
208Song title: Fire
209Artist: Seth Power
210Album title: Magnolia Soul
211Track number: 3
212Genre: 85. sentimental
213Date: 2017
214Picture: MIME type: "image/jpg", "Cover Image", 32986 bytes
215Copyright information: http://creativecommons.org/licenses/by/3.0/
216Publisher webpage: http://www.jamendo.com
217Rating: ***
218Play counter: 100
219Überschreiben Sie die Methoden public boolean equals(Object obj) und public int hashCode() so, dass zwei Metadaten Onjekte genau dann gleich sind, wenn alle enthaltenen Daten gleich sind.
220
2211.2 Kern-Klassen
222Die folgenden Klassen bilden den Kern der Anwendung.
223Alle folgenden Klasse soll im im Paket jpp.wuetunes.model erstellt werden.
224
2251.2.1 Die Klasse Song
226Diese Klasse modelliert ein Musikstück inklusive dessen Metadaten. Die Audiodaten selbst werden nicht gespeichert, sondern nur über einen Verweis auf die entsprechende Datei referenziert.
227Die Klasse soll das Interface Comparable<Song> direkt implementieren, so dass nach Künstler, Titel des Albums, Nummer des Musikstücks auf dem Album und Titel des Musikstücks verglichen wird. Falls ein Wert nicht gesetzt ist, verwenden Sie für den Vergleich einen leeren String bzw. 0.
228Implementieren Sie mindestens den folgenden Konstruktor und die folgenden Methoden:
229
230public Song(Path filePath, Metadata metadata)
231Erstellt ein Musikstück mit dem auf die Musikdatei verweisenden Pfad und Metadaten.
232Befindet sich unter filePath keine reguläre Datei, deren Name auf ".mp3" endet, soll eine IllegalArgumentEception geworfen werden.
233public Metadata getMetadata()
234Gibt die Metadaten des Musikstücks zurück. Es soll dabei dieselbe Instanz zurückgegeben werden, die dem Konstruktor bei der Erzeugung des Musikstücks übergeben wurde (also keine Kopie).
235public Path getFilePath()
236Gibt den Pfad zurück.
237public String toString()
238Gibt eine String Repräsentation eines Musikstücks der folgenden Form zurück.
239
240Filename: <Dateiname des Musikstücks>
241<String Repräsentation der Metadaten>
242
243Als Zeilentrennzeichen soll dabei \n verwendet werden.
244
245Beispiel:
246Filename: Seth_Power_-_Fire.mp3
247Song title: Fire
248Artist: Seth Power
249Album title: Magnolia Soul
250Track number: 3
251Genre: 85. sentimental
252Date: 2017
253Picture: MIME type: "image/jpg", "Cover Image", 32986 bytes
254Copyright information: http://creativecommons.org/licenses/by/3.0/
255Publisher webpage: http://www.jamendo.com
256Rating: ***
257Play counter: 100
258Überschreiben Sie die Methoden public boolean equals(Object obj) und public int hashCode() so, dass zwei Musikstücke genau dann gleich sind, wenn der zugehörige Pfad gleich ist.
259Dies bedeutet, dass die durch die Methode compareTo() definierte natürliche Ordnung nicht konsistent zur Methode equals ist (vgl. Dokumention Comparable).
260
2611.2.2 Die Klasse SongLibrary
262Diese Klasse modelliert eine Sammlung von Musikstücken.
263Erstellen Sie in der Klasse mindestens die folgenden Konstruktoren und Methoden:
264
265public SongLibrary()
266Erzeugt eine Sammlung ohne Musikstücke.
267public SongLibrary(Collection<Song> songs)
268Erzeugt eine Sammlung mit den übergebenen Musikstücken.
269Die übergebene Collection darf (auch bei Aufrufen weiterer Methoden dieser Klasse) nicht verändert werden.
270(Enthält die Collection gleiche Musikstücke, soll keine Exception geworfen werden.)
271public void add(Song song)
272Fügt der Sammlung ein Musikstück hinzu.
273(Ist das Musikstück bereits enthalten, soll keine Exception geworfen und die Sammlung nicht verändert werden.)
274public void addAll(Collection<Song> songs)
275Fügt der Sammlung eine Collection von Musikstücken hinzu.
276Die übergebene Collection darf (auch bei Aufrufen weiterer Methoden dieser Klasse) nicht verändert werden.
277(Ist eines der übergebenen Musikstücke bereits enthalten oder enthält die Collection gleiche Musikstücke, soll keine Exception geworfen werden.)
278public boolean remove(Song song)
279Entfernt das übergebene Musikstück aus der Sammlung.
280Gibt true zurück, falls das Musikstück tatsächlich enthalten war und somit tatsächlich entfernt wurde, false andernfalls.
281(Ist das Musikstück nicht enthalten, soll keine Exception geworden werden.)
282public boolean removeAll(Collection<Song> songs)
283Entfernt die Musikstücke der übergebenen Collection aus der Sammlung.
284Gibt true zurück, falls mindestens ein Musikstück tatsächlich enthalten war und somit tatsächlich entfernt wurde, false andernfalls.
285Die übergebene Collection darf (auch bei Aufrufen weiterer Methoden dieser Klasse) nicht verändert werden.
286(Ist keines der Musikstücke enthalten, soll keine Exception geworden werden.)
287public Set<Song> getSongs()
288Gibt eine Menge der in der Sammlung enthaltenen Musikstücken zurück.
289Enthält die Sammlung keine Musikstücke, soll eine leere Liste zurückgegeben werden.
290public Set<Song> getSongs(Predicate<Song> filter)
291Gibt eine Menge der Musikstücke zurück, die vom übergebenen Filter akzeptiert werden. Der Filter ist als Prädikat implementiert, das für zu akzeptierende Musikstücke true zurück geben soll.
292Enthält die Sammlung keine Musikstücke, soll eine leere Liste zurückgegeben werden.
293
294Hinweis:
295Implementierungen der Schnittstelle java.util.function.Predicate definieren jeweils eine Funktion mit einem Parameter, die true oder false zurückgibt. Wie alle Schnittstellen im Paket java.util.function ist auch Predicate ein FunctionalInterface, das also als Lambda-Ausdruck notiert werden kann.
296Ein Aufruf der Methode, der alle Musikstücke zurück gibt, deren Titel mit dem Buchstaben M beginnen, kann also wie folgt notiert werden:
297
298getSongsByFilter(song -> song.getMetadata().getSongTitle().orElse("").startsWith("M"));
299
300Dies ist semantisch äquivalent zu:
301
302getSongsByFilter(new Predicate<Song>() {
303 @Override
304 public boolean test(Song song) {
305 return song.getMetadata().getSongTitle().orElse("").startsWith("M");
306 }
307 });
308
309Lesen Sie dazu ggf. Javabuch: Kapitel 11 Lambda-Ausdrücke und funktionale Programmierung und Oracle Java Tutorials: Lambda Expressions.
3101.2.3 Die Klasse SongFilterFactory
311Für die Methode getSongsByFilter(Predicate<Song> filter) der Klasse SongLibrary lassen sich auch Filter vordefinieren.
312Implementieren Sie dazu die Klasse SongFilterFactory, die einige statische Methoden zur Verfügung stellt, um Filter zu erzeugen und durch und oder oder zu verknüpfen.
313Implementieren Sie mindestens die folgenden Methoden:
314
315public static Predicate<Song> getArtistFilter(String artist)
316Gibt einen Filter zurück, der ein Musikstück genau dann akzeptiert, wenn dessen Künstler gleich dem übergebenen ist.
317public static Predicate<Song> getAlbumTitleFilter(String albumTitle)
318Gibt einen Filter zurück, der ein Musikstück genau dann akzeptiert, wenn dessen Albumtitel gleich dem übergebenen ist.
319public static Predicate<Song> getGenreFilter(Genre genre)
320Gibt einen Filter zurück, der ein Musikstück genau dann akzeptiert, wenn dessen Genre gleich dem übergebenen ist.
321public static Predicate<Song> getMinRatingFilter(int rating)
322Gibt einen Filter zurück, der ein Musikstück genau dann akzeptiert, wenn dessen Bewertung größer oder gleich der übergebenen ist.
323Ist die übergebene Zahl kleiner als 0 oder größer als 5, soll eine java.lang.IllegalArgumentException geworfen werden.
324Beachten Sie, dass eine nicht gesetzte Bewertung an dieser Stelle nicht der Bewertung 0 entspricht.
325public static Predicate<Song> combineAnd(Collection<Predicate<Song>> filters)
326Gibt einen Filter zurück, der ein Musikstück genau dann akzeptiert, wenn es alle in der übergebenen Collection enthaltenen Filter akzeptiert.
327Ist die übergebene Collection leer, soll eine java.lang.IllegalArgumentException geworfen werden.
328Tip: Nutzen Sie die Default-Methode and der Schnittstelle Predicate.
329public static Predicate<Song> combineOr(Collection<Predicate<Song>> filters)
330Gibt einen Filter zurück, der ein Musikstück genau dann akzeptiert, wenn es mindestens einer der in der übergebenen Collection enthaltenen Filter akzeptiert.
331Ist die übergebene Collection leer, soll eine java.lang.IllegalArgumentException geworfen werden.
332Tip: Nutzen Sie die Default-Methode or der Schnittstelle Predicate.
333Achten Sie darauf, dass die Ausführung der hier generierten Filter auch dann keine Exception wirft, wenn die jeweilige Information (z. B. der Künstler) in
334den Metadaten des Musikstücks nicht vorliegt, wenn also z. B. Metadata.getArtist() ein Optional.empty() zurückgibt.
335
3361.2.4 Die Klasse Playlist
337Modelliert eine Wiedergabeliste von Musikstücken. Eine Wiedergabeliste kann das gleiche Musikstück mehrfach enthalten.
338Die Klasse stellt Methoden bereit, die stets das aktuelle bzw. nächste und vorige Musikstück setzen bzw. zurückgeben können.
339Der öffentliche Standardkonstruktor soll eine Wiedergabeliste erzeugen, die nicht im Zufallsmodus ist.
340
341public void add(Song song)
342Fügt ein Musikstück an das Ende der Wiedergabeliste an.
343Der Aufruf der Methode darf das aktuelle Musikstück nicht verändern.
344public List<Song> getSongs()
345Gibt eine Liste der enthaltenen Musikstücke in der Reihenfolge des Hinzufügens zurück.
346public void addAt(int index, Song song)
347Fügt der Wiedergabeliste ein Musikstück an der übergebenen Position zu.
348Der Aufruf der Methode darf das aktuelle Musikstück nicht verändern.
349Ist die Position kleiner als 0 oder größer als die Anzahl der bereits enthaltenen Musikstücke, soll eine java.lang.IllegalArgumentException geworfen werden.
350public void removeAt(int index)
351Entfernt das an der übergebenen Position vorhandene Musikstück. Ist ein Musikstück mehrfach in der Wiedergabeliste enthalten, sollen nicht alle entfernt werden.
352Befindet sich an der Position das aktuelle Musikstück, soll für die Wiedergabeliste kein aktuelles Musikstück mehr gesetzt sein.
353Der Aufruf der Methode darf das aktuelle Musikstück ansonsten nicht verändern.
354Ist die Position kleiner als 0 oder nicht kleiner als die Anzahl der bereits enthaltenen Musikstücke, soll eine java.lang.IllegalArgumentException geworfen werden.
355public void setCurrent(int index)
356Legt das an der übergebenen Position befindliche Musikstück als aktuelles Musikstück fest.
357Wird die Methode mit -1 aufgerufen, soll die Wiedergabeliste danach kein aktuelles Musikstück mehr besitzen.
358Ist die Position kleiner als -1 oder nicht kleiner als die Anzahl der bereits enthaltenen Musikstücke, soll eine java.lang.IllegalArgumentException geworfen werden.
359public Optional<Integer> getCurrent()
360Gibt die Position des aktuellen Musikstücks zurück.
361Ist die Wiedergabeliste leer oder wurde noch kein aktuelles Musikstück festgelegt, wird ein leeres Optional zurückgegeben.
362public Optional<Song> getCurrentSong()
363Gibt ein Optional des aktuellen Musikstücks zurück.
364Ist die Wiedergabeliste leer oder wurde noch kein aktuelles Musikstück festgelegt, wird ein leeres Optional zurückgegeben.
365public void setRandom(Random random)
366Setzt ein java.util.Random Objekt.
367Dieses Random-Objekt darf nur in der Methode getRandom() verwendet werden, und zwar je Aufruf von getRandom in Form eines einmaligen Aufrufs der Methode Radom#nextInt(int bound).
368Weitere Aufrufe von Methoden des Random-Objekts sind also nicht erlaubt.
369public Optional<Integer> getRandom()
370Gibt eine zufällig ermittelte, gültige Position der Wiedergabeliste zurück. Die Position soll dabei der Position entsprechen, die von der Methode Random#nextInt(int bound) auf dem Random-Objekt der Wiedergabeliste aufgerufen wird, wobei bound der Anzahl der Musikstücke in der Wiedergabeliste entspricht.
371Wurde kein Random-Objekt gesetzt, soll ein beliebiges Random-Objekt verwendet werden.
372Ist die Wiedergabeliste leer, soll ein leeres Optional zurückgegeben werden.
373public void setShuffle(boolean value)
374Legt fest, ob die Wiedergabeliste in den Zufallsmodus gesetzt werden soll.
375Beim Aufruf der Methoden next() und previous() soll in diesem Fall ein zufälliges Musikstück zurückgegeben werden.
376public Optional<Integer> next()
377Gibt die Position des nächsten Musikstücks wie folgt zurück und setzt dieses Musikstück als neues aktuelles Musikstück:
378Ist die Wiedergabeliste leer, soll ein leeres Optional zurückgegeben werden.
379Falls die Wiedergabeliste nicht im Zufallsmodus ist, gilt folgendes:
380Gibt 0 zurück, wenn kein aktuelles Musikstück gesetzt ist oder wenn das aktuelle Musikstück das letzte der Wiedergabeliste ist.
381Gibt andernfalls die Position des nächsten Musikstücks zurück, also jenen Musikstücks, das direkt nach dem aktuellen Musikstück eingefügt wurde.
382Falls die Wiedergabeliste im Zufallsmodus ist, gilt fogendes:
383Gibt die Position zurück, die von der Methode getRandom zurückgegeben wird.
384Die Methode getRandom darf während eines Aufrufs der Methode next() nur einmal aufgerufen werden.
385public Optional<Integer> previous()
386Gibt ein die Position des vorigen Musikstücks wie folgt zurück und setzt dieses Musikstück als neues aktuelles Musikstück:
387Ist die Wiedergabeliste leer, soll ein leeres Optional zurückgegeben werden.
388Falls die Wiedergabeliste nicht im Zufallsmodus ist, gilt folgendes:
389Gibt die Position des letzten Musikstücks in der Wiedergabeliste zurück, wenn kein aktuelles Musikstück gesetzt ist oder wenn das aktuelle Musikstück das erste der Wiedergabeliste ist.
390Gibt andernfalls die Position des vorigen Musikstücks zurück, also jenen Musikstücks, das direkt vor dem aktuellen Musikstück eingefügt wurde.
391Falls die Wiedergabeliste im Zufallsmodus ist, gilt fogendes:
392Gibt die Position zurück, die von der Methode getRandom zurückgegeben wird.
393Die Methode getRandom darf während eines Aufrufs der Methode previous() nur einmal aufgerufen werden.
394Teilaufgabe 2: Auslesen, Verwalten und Konvertieren von ID3-Tags aus MP3-Dateien
395In MP3-Dateien werden die Metadaten in Form eines ID3-Tags gespeichert. Die aktuelle Version ist v2.4., auf die wir uns im Rahmen dieser Aufgabe beschränken wollen. Es werden weiterhin einige vereinfachende Annahmen getroffen, was dazu führt, dass nicht jede MP3-Datei korrekt eingelesen werden kann.
396Lesen Sie vor der Implementierung die ID3Tag-Dokumentation.
397
398Ihnen werden für lokale Tests nur solche MP3-Dateien zur Verfügung gestellt, die den Annahmen entsprechen. Natürlich werden auch für die PABS-Tests nur solche MP3-Dateien verwendet.
399Es sollen nun Klassen implementiert werden, die einen ID3-Tag aus einer MP3-Datei auslesen und in ein Metadata Objekt aus Teilaufgabe 1 konvertieren können. Einige der Klassen erscheinen ähnlich zu einigen Klassen aus Teilaufgabe 1, da Sie im wesentlichen die gleichen Daten halten.
400
4012.1 Hilfsklassen
4022.1.1 Die Klasse ID3Utils
403Diese Klasse stellt einige statische Methoden zur Verfügung, die zum Auslesen eines ID3-Tags in verschiedenen Klassen verwendet werden können.
404Lesen Sie dazu ggf. Javabuch: Kapitel 22.1 Bits und Bytes.
405Die Klasse ist Ihnen im Paket jpp.wuetunes.io.files.id3 bereits vorgegeben. Die vorhanden Methoden können in späteren Teilaufgaben verwendet werden. Implementieren Sie zusätzlich die folgenden Methoden:
406
407static String byteToBitString(byte b)
408Gibt einen Bitstring mit führenden Nullen (also stets 8 Zeichen) zurück, der das übergebene byte repräsentiert. Das höchstwertige Bit soll dabei links stehen.
409Beispiele:
410übergebener Wert -> Rückgabewert
411 -128 -> "10000000"
412 -87 -> "10101001"
413 -1 -> "11111111"
414 0 -> "00000000"
415 1 -> "00000001"
416 16 -> "00010000"
417 127 -> "01111111"
418static String intToBitString(int value)
419Gibt einen Bitstring mit führenden Nullen (also stets 32 Zeichen) zurück, der das übergebene int repräsentiert. Das höchstwertige Bit soll dabei links stehen.
420Beispiele:
421übergebener Wert -> Rückgabewert
422 -2147483648 -> "10000000000000000000000000000000"
423 -128 -> "11111111111111111111111110000000"
424 -1 -> "11111111111111111111111111111111"
425 0 -> "00000000000000000000000000000000"
426 1 -> "00000000000000000000000000000001"
427 71 -> "00000000000000000000000001000111"
428 2147483647 -> "01111111111111111111111111111111"
429
430Diese Methode wird für die Implementierung der weiteren Klassen dieser Teilaufgabe im Normalfall nicht benötigt. Sie können sie dazu verwenden, um ihren Code zu debuggen.
431static boolean readFlag(byte value, int flagPosition)
432Wird ein Byte als Bitstring interpretiert, kann er dazu verwendet werden, um bis zu 8 sogenannte "Flags" zu speichern.
433Die Methode gibt zurück, ob beim übergebenen Byte das Flag an der übergebenen Position gesetzt ist. Position 7 ist dabei das am weitesten links stehende, Position 0 das am weitesten rechts stehende Bit.
434Ist die übergebene Position größer als 7 oder kleiner als 0, soll eine java.lang.IllegalArgumentException geworfen werden.
435Beispiele:
436Zur Verdeutlichung wird der Wert des übergebenen Bytes hier als Binäres Literal notiert.
437übergebene Werte -> Rückgabewert
438 0b00000000, 0 -> false
439 0b00000001, 0 -> true
440 0b00000001, 1 -> false
441 0b01000000, 6 -> true
442 0b10001001, 0 -> true
443 0b10001001, 2 -> false
444 0b10001001, 3 -> true
445 0b10001001, 7 -> true
446static int read32BitSynchsafeInteger(byte[] data)
447Liest aus einem übergebenen Array der Länge 4 (32 bit) ein int aus, das als "synchsafe integer" gespeichert ist.
448Ist die Länge des Arrays nicht genau 4, soll eine java.lang.IllegalArgumentException geworfen werden.
449Beispiele:
450Zur übersichtlichen Darstellung wird der übergebene Array mit einzelnen Datenwerten in hexadezimaler Schreibweise ohne Typkonversion angegeben.
451Eine Angabe von 0xDD, 0xF1, 0x21, 0xB9 entspricht also new byte[]{(byte)0xDD, (byte)0xF1, (byte)0x21, (byte)0xB9}.
452übergebener Wert -> Rückgabewert
4530x00, 0x00, 0x00, 0x00 -> 0
4540x00, 0x00, 0x00, 0x01 -> 1
4550x00, 0x00, 0x00, 0x09 -> 9
4560x00, 0x00, 0x00, 0x7F -> 127
4570x00, 0x00, 0x00, 0xFF -> 127
4580xDD, 0xF1, 0x21, 0xB9 -> 196890809
4590x95, 0x04, 0xBC, 0x3C -> 44113468
4600xD1, 0x8C, 0xAB, 0xFD -> 170071549
4610xAA, 0xF0, 0x33, 0x4D -> 89921997
4620x16, 0xD1, 0xD7, 0x40 -> 47475648
463
464Siehe Anhang A.1 der ID3Tag Dokumentation.
4652.2 Modell-Klassen für ID3
466Diese Klassen modellieren die in der ID3Tag-Dokumentation beschriebenen Entitäten.
467In später zu implementierenden Klasseen werden diese Entitäten aus MP3-Dateien ausgelesen und mittels eines Konverters in Metadata-Objekte konvertiert.
468Erstellen Sie die folgenden Klassen im Paket jpp.wuetunes.io.files.id3.
469
4702.2.1 Der Aufzählungstyp ID3FrameContentType
471Ein ID3 Frame hat genau einen Inhaltstyp. Der Inhalt ist entweder Text, eine URL oder ein Bild.
472Erstellen Sie dazu eine enum mit den Konstanten TEXT_INFORMATION, URL_LINK, und ATTACHED_PICTURE.
473
4742.2.2 Der Aufzählungstyp ID3FrameType
475Ein ID3 Frame ist immer von genau einem Typ, welche Information der Inhalt angibt.
476Jeder Typ hat genau einen Inhaltstyp. Z. B. hat der Typ TIT2 (Titel des Musikstücks) immer den Inhaltstyp TEXT_INFORMATION.
477Es soll nun eine enum erstellt und für jeden unterstützten Frame Typ eine eine Konstante definiert werden, deren Namen identisch zum den entsprechenden Identifizierern sind.
478Erstellen Sie also die Konstanten TPE1, TIT2, TALB, TCON, TDRC, TRCK, WCOP, WPUB, und APIC.
479Implementieren Sie außerdem die folgenden Methoden:
480
481public ID3FrameContentType getContentType()
482Gibt den Inhaltstyp wie folgt zurück:
483Konstante -> Rückgabewert
484 TPE1 -> TEXT_INFORMATION
485 TIT2 -> TEXT_INFORMATION
486 TALB -> TEXT_INFORMATION
487 TCON -> TEXT_INFORMATION
488 TDRC -> TEXT_INFORMATION
489 TRCK -> TEXT_INFORMATION
490 WCOP -> URL_LINK
491 WPUB -> URL_LINK
492 APIC -> ATTACHED_PICTURE
493static Optional<ID3FrameType> getByIdentifier(String identifier)
494Gibt ein Optional des Frame-Typs zurück, dessen Name gleich des übergebenen Bezeichners ist.
495Gibt ein leeres Optional zurück, falls kein Frame Typ mit diesem Namen existiert.
496Der Inhaltstyp kann den Konstanten des Aufzählungstyps ID3FrameType in einem zu implementierenden Konstruktor übergeben werden.
497
4982.2.3 Die Klasse ID3FrameHeader
499Die Klasse modelliert den Header eines ID3-Frames.
500Implementieren Sie den folgenden Konstruktor und die folgenden Methoden:
501
502ID3FrameHeader(ID3FrameType type, int size, byte statusFlags, byte formatFlags)
503Erzeugt einen Header eines ID3-Frames mit dem übergebenen Typ, der übergebenen Größe und den Format- und Status-Flags.
504Ist die Größe nicht positiv, soll eine java.lang.IllegalArgumentException geworfen werden.
505public boolean isFlagDataLengthIndicator()
506Gibt zurück, ob das Flag "Data length indicator" gesetzt ist.
507public boolean isFlagUnsynchronization()
508Gibt zurück, ob das Flag "Unsynchronisation" gesetzt ist.
509public ID3FrameType getFrameType()
510Gibt den Frame-Typ zurück.
511public int getSize()
512Gibt die Größe zurück.
513public String toString()
514Gibt eine String Repräsentation des Frame-Headers der folgenden Form zurück:
515[ID3FrameHeader <Name des Frame Typs>, size: <Größe>, status flags: <Bitstring Status-Flags>, format flags: <Bitstring Format-Flags>, data length indicator: <Wert Flag Data length indicator>, unsynchronization: <Wert Flag Unsynchronization>]
516Beispiele:
517[ID3FrameHeader TPE1, size: 12, status flags: 00000000, format flags: 00000000, data length indicator: false, unsynchronization: false]
518[ID3FrameHeader APIC, size: 17293, status flags: 00000000, format flags: 00000011, data length indicator: true, unsynchronization: true]
5192.2.4 Die Schnittstelle ID3FrameBody
520Diese Schnittstelle muss von allen Klassen implementiert werden, die einen ID3 Frame Body modellieren.
521Implementieren Sie das interface ID3FrameBody, das nur eine Methode vorgibt:
522
523String dataToString()
5242.2.5 Die Klasse ID3FrameBodyTextInformation
525Die Klasse modelliert den Body eines Frames, dessen Inhalt Text ist.
526Die Klasse soll die Schnittstelle ID3FrameBody implementieren.
527Immplementieren Sie den folgenden Konstruktor und die folgenden Methoden:
528
529ID3FrameBodyTextInformation(String text)
530Erstellt einen ID3FrameBodyTextInformation mit dem übergebenen Text.
531public String getText()
532Gibt den Text zurück.
533public String dataToString()
534Überschreibt die Methode der Schnittstelle so, das ein String der folgenden Form zurückgegeben wird:
535[ID3FrameBody Text "<Text>"].
536(Die Anführungszeichen sollen also ebenfalls zurückgegeben werden.)
5372.2.6 Die Klasse ID3FrameBodyURLLink
538Die Klasse modelliert den Body eines Frames, dessen Inhalt eine URL ist.
539Die Klasse soll die Schnittstelle ID3FrameBody implementieren.
540Immplementieren Sie den folgenden Konstruktor und die folgenden Methoden:
541
542ID3FrameBodyURLLink(URL url)
543Erstellt einen ID3FrameBodyURLLink mit der übergebenen URL.
544public URL getUrl()
545Gibt die URL zurück.
546public String dataToString()
547Überschreibt die Methode der Schnittstelle so, das ein String der folgenden Form zurückgegeben wird:
548"[ID3FrameBody URL <String-Repräsentation der URL>"].
549(Die Anführungszeichen sollen also ebenfalls zurückgegeben werden.)
5502.2.7 Die Klasse ID3FrameBodyAttachedPicture
551Die Klasse modelliert den Body eines Frames, dessen Inhalt ein Bild ist.
552Die Klasse soll die Schnittstelle ID3FrameBody implementieren.
553Implementieren Sie den folgenden Konstruktor und die folgenden Methoden:
554
555ID3FrameBodyAttachedPicture(String mimeType, int imageType, String description, byte[] data)
556Erstellt einen ID3FrameBodyAttachedPicture mit dem übergebenen MIME-Typ, dem übergebenen Bild-Typ, der übergebenen Bildbeschreibung, und den übergebenen rohen Bilddaten.
557Falls der mimeType kein valider MIME-Typ ist, oder wenn der Bild-Typ nicht einer der unterstützten Bild-Typen (also zwischen 0 und 20) ist, oder der Array mit den Bilddaten leer ist, soll eine java.lang.IllegalArgumentException geworfen werden.
558public String getMimeType()
559Gibt den MIME-Typ zurück.
560public int getImageType()
561Gibt den Bild-Typ zurück.
562public String getDescription()
563Gibt die Bildbeschreibung zurück.
564public byte[] getPictureData()
565Gibt die Bilddaten zurück.
566public String dataToString()
567Überschreibt die Methode der Schnittstelle so, das ein String der folgenden Form zurückgegeben wird:
568[ID3FrameBody Picture MIME type: "<MIME Type>", image type: <Bild-Typ>, "<Bildbeschreibung>", <Länge der Bilddaten> bytes, picture hash: <Hash code der Bilddaten>]
569Beispiel:
570[ID3FrameBody Picture MIME type: "image/jpg", image type: 0, "Front Cover", 12345 bytes, picture hash: 76382187]
571Hinweis:
572Verwenden Sie für die Berechnung des Hash codes der Bilddaten die statische Methode hashCode der Klasse java.util.Arrays.
573(Überlegen Sie sich, wieso die Methode hashCode des byte-Arrays hier nicht verwendet werden kann!)
574Eine Änderung der im Konstruktor übergebenen Bilddaten (außerhalb dieser Klasse) darf den Zustand der Instanz dieser Klasse nicht verändern.
575Eine Änderung der mit der Methode getPictureData zurückgegebenen Bilddaten (außerhalb dieser Klasse) darf den Zustand der Instanz dieser Klasse nicht verändern.
576
5772.2.8 Die Klasse ID3Frame
578Die Klasse modelliert einen Frame eines ID3Tags. Die Klasse soll als generischer Typ implementiert werden, wobei B der Typparameter ist. Als Typ sollen nur Klassen zugelassen sein, die die Schnittstelle ID3FrameBody implementieren.
579Lesen Sie dazu ggf. Javabuch: Kapitel 10 Generics<T> und Oracle Java Tutorials: Generic Types.
580Immplementieren Sie den folgenden Konstruktor und die folgenden Methoden:
581
582ID3Frame(ID3FrameHeader header, B body)
583Erstellt einen Frame mit dem übergebenen ID3Header und dem übergebenen Instanz einer Klasse, die ID3FrameBody implementiert.
584public ID3FrameHeader getHeader()
585Gibt den Header zurück.
586public B getBody()
587Gibt den Body zurück.
588public String toString()
589Gibt eine String Repräsentation des Frame-Headers der folgenden Form zurück:
590<String-Repräsentation des Headers><Rückgabewert der Methode dataToString() des Bodies>
591Beispiele:
592[ID3FrameHeader TPE1, size: 12, status flags: 00000000, format flags: 00000000, data length indicator: false, unsynchronization: false][ID3FrameBody Text "Ed Sheeran"]
593[ID3FrameHeader APIC, size: 17293, status flags: 00000000, format flags: 00000011, data length indicator: true, unsynchronization: true][ID3FrameBody Picture MIME type: "image/jpg", image type: 0, "Front Cover", 12345 bytes]
5942.2.9 Die Klasse ID3TagHeader
595Die Klasse modelliert den Header eines ID3Tags.
596Implementieren Sie den folgenden Konstruktor und die folgenden Methoden:
597
598ID3TagHeader(int majorVersion, int revision, boolean flagUnsynchronisation, boolean flagExtendedHeader, boolean flagExperimentalIndicator, boolean flagFooterPresent, int tagSize)
599Erstellt einen Header mit den übergebenen Daten.
600Ist die majorVersion, die revision, oder die tagSize negativ, soll eine java.lang.IllegalArgumentException geworfen werden.
601public int getMajorVersion()
602Gibt die Hautersionsnummer zurück.
603public int getRevision()
604Gibt die Revisionsnummer zurück.
605public boolean isFlagUnsynchronisation()
606Gibt zurück, ob das Flag Unsynchronisation gesetzt ist.
607public boolean isFlagExtendedHeader()
608Gibt zurück, ob das Flag Extended header gesetzt ist.
609public boolean isFlagExperimentalIndicator()
610Gibt zurück, ob das Flag Experimental indicator gesetzt ist.
611public boolean isFlagFooterPresent()
612Gibt zurück, ob das Flag Footer present gesetzt ist.
613public int getTagSize()
614Gibt die Größe des Tags zurück.
615public String toString()
616Gibt eine String Repräsentation des Frame-Headers der folgenden Form zurück:
617[ID3TagHeader version: ID3v2.<Hauptversionsnummer>.<Revisionsnummer>, unsynchronisation: <Wert Flag unsynchronisation>, extended header: <Wert Flag Extended header>, experimental indicator: <Wert Flag Experimental indicator>, footer present: <Wert Flag Footer present>, tag size: <Größe des Tags> bytes]
618
619Beispiel:
620[ID3TagHeader version: ID3v2.2.4, unsynchronisation: false, extended header: false, experimental indicator: false, footer present: true, tag size: 24680 bytes]
6212.2.10 Die Klasse ID3Tag
622Die Klasse modlliert einen ID3Tag.
623Implementieren Sie den folgenden Konstruktor und die folgenden Methoden.
624
625ID3Tag(ID3TagHeader header, List<ID3Frame> frames)
626Erzeugt einen ID3Tag mit dem übergebenen Header und der übergebenen Liste mit Frames.
627public ID3TagHeader getHeader()
628Gibt den Header zurück.
629public List<ID3Frame> getFrames()
630Gibt die Liste der Frames zurück.
631public List<ID3Frame> getOrderedFrames(ID3FrameType type)
632Gibt eine entsprechend der im Konstruktor übergebenen Liste geordnete Liste zurück, die nur solche Frames enthält, dessen Header den übergebenen Frame Type haben.
633public Optional<ID3Frame> getFrameByIdentifier(ID3FrameType type)
634Gibt ein Optional des ersten Frames aus der im Konstruktor übergebenen Liste zurück, dessen Header den übergebenen Frame Type hat.
635Existiert kein solcher Frame, soll ein leeres Optional zurückgegeben werden.
636public String toString()
637Gibt eine String Repräsentation des Frame-Headers der folgenden Form zurück:
638<String-Repräsentation des Headers>
639<String-Repräsentation des 1. Frames>
640<String-Repräsentation des 2. Frames>
641...
642<String-Repräsentation des n. Frames>
643Verwenden Sie "\n" als Zeilentrenner.
644Beachten Sie, dass bei einem Tag ohne Frames die Zeile mit der String-Repräsentation des Headers ohne Zeilentrenner zurückgegeben werden soll.
645Eine Änderung der im Konstruktor übergebenen Liste mit Frames (außerhalb dieser Klasse) darf den Zustand der Instanz dieser Klasse nicht verändern.
646Eine Änderung der mit der Methode getFrames zurückgegebenen Liste mit Frames (außerhalb dieser Klasse) darf den Zustand der Instanz dieser Klasse nicht verändern.
647
6482.2.11 Die Klasse ID3Encoding
649Die Klasse modelliert eine bei ID3-Tags verwendete Kodierung zum Schreiben und Auslesen von Text.
650Implementieren Sie den folgenden Konstruktor und die folgenden Methoden:
651
652ID3Encoding(Charset charSet, boolean withBOM, int numNullBytes)
653Erzeugt eine Kodierung mit dem übergebenen Zeichensatz. Der Parameter withBOM gibt an, ob bei der Kodierung ein Byte Order Mark (BOM) verwendet wird. Der Parameter numNullBytes gibt an, mit wie vielen Null-Bytes (also bytes mit dem Wert 0x00) die Byte-Folge beendet wird.
654Ist numNullBytes nicht 1 oder 2, soll eine java.lang.IllegalArgumentException geworfen werden.
655public Charset getCharset()
656Gibt den von der Kodierung verwendeten Zeichensatz zurück.
657Hinweis: Beachten Sie die Klasse java.nio.charset.StandardCharsets aus der Java API.
658public boolean isWithBOM()
659Gibt zurück, ob die Kodierung ein BOM verwendet.
660public int getNumNullBytes()
661Gibt zurück, mit wie vielen Null-Bytes die mit dieser Kodierung kodierte Byte-Folge endet.
662Siehe Abschnitt 2.3.2.1 der ID3Tag-Dokumentation.
663
6642.3 Klassen zum Auslesen von ID3-Tags aus MP3-Dateien
665Es folgt nun die Implementierung der Klassen, die für das tatsächliche Auslesen und Konvertieren von ID3Tags verantwortlich sind.
666Nutzen Sie bei der Implementierung dieser Klassen und bei eigenen Tests einen Hex-Editor zum Betrachten der Rohdaten der MP3-Dateien und ein Programm zum Anzeigen von ID3Tags!
667Im WueCampus-Kurs stehen Ihnen für lokale Tests solche MP3-Dateien als Download zur Verfügung, bei denen sichergestellt ist, dass sie den Annahmen aus der ID3Tag-Dokumentation entsprechen.
668
6692.3.1. Die Klasse ID3TagReaderException
670Diese Exception soll geworfen werden, wenn während des Auslesens von ID3-Tags eine Ausnahme auftritt.
671Die Klasse soll die java.io.IOException erweitern.
672Implementieren Sie die folgenden Konstruktoren:
673
674ID3TagReaderException(String message)
675Erzeugt eine ID3TagReaderException so, dass die übergebene Nachricht beim Aufruf der durch Vererbung bereitsgestellten Methode getMessage zurückgegeben wird.
676Wenn die Nachricht leer ist, soll eine java.lang.IllegalArgumentException geworfen werden.
677ID3TagReaderException(String message, Throwable cause)
678Erzeugt eine ID3TagReaderException so, dass die übergebene Nachricht bzw. der übergebene Grund beim Aufruf der durch Vererbung bereitsgestellten Methoden getMessage bzw. getCause() zurückgegeben wird.
679Wenn die Nachricht leer ist, soll eine java.lang.IllegalArgumentException geworfen werden.
6802.3.2 Die Klasse ID3TagReader
681Dies ist die Hauptklasse für das Auslesen von ID3-Tags aus MP3-Dateien.
682Die Methoden dieser Klasse sind statisch. Bedenken Sie, dass auch wiederholt oder parallel ausgeführte Aufrufe von diesen Methoden die korrekten Ergebnisse liefern müssen. Es wird daher dringend empfohlen, die Verwendung von statischen, nicht finalen Feldern zu vermeiden. Die Verwendung von finalen Variablen dagegen kann natürlich sinnvoll sein, sofern die referenzierten Objekte immutable und somit inhaltlich nicht veränderbar sind.
683Verwenden Sie bei der Implementierung dieser Methode, die weiteren zu implementierenden Methoden:
684
685private static ID3Encoding readEncoding(byte encoding, byte bom0, byte bom1) throws ID3TagReaderException
686Gibt für die drei übergebenen Bytes das korrekte ID3Encoding zurück. Für Kodierungen, die kein BOM verwendet, wird entsprechend nur das erste Byte berücksichtigt.
687Falls für die übergebenen Bytes kein Encoding definiert ist, soll eine ID3TagReaderException geworfen werden.
688private static ID3TagHeader readTagHeader(byte[] data) throws ID3TagReaderException
689Liest aus einem Byte Array einen ID3TagHeader aus.
690Hat der Array nicht die Länge 10, soll eine java.lang.IllegalArgumentException geworfen werden.
691Hat der Array die Länge 10, aber sind die ersten drei Bytes des Arrays nicht [0x49 0x44 0x33], soll eine ID3TagReaderException geworfen werden.
692private static int readFrame(byte[] framesData, int offset, List<ID3Frame> frames) throws ID3TagReaderException
693Falls in den ersten 4 Bytes (ab offset) in framesData der Identifier eines unterstütztes Frame-Typ (TPE1, TIT2,...) steht, soll der ID3Frame ausgelesen aus den nachfolgenden Bytes ausgelesen und der übergebenen Liste frames an deren Ende hinzugefügt werden.
694Falls in den ersten 4 Bytes (ab offset) in framesData kein Identifier eines unterstützten Frame-Typs steht, soll kein ID3Frame ausgelesen und nichts der Liste frames hinzugefügt werden. Die übergebene Liste darf ansonsten nicht modifiziert werden.
695Verwenden Sie entsprechend der ID3Tag-Dokumentation die Methode ID3Utils.synchronize, um bei entsprechend gesetztem Flag in Frames des Typs APIC die Bilddaten zu synchronisieren.
696Gibt den Index in framesData zurück, ab dem der nächste Frame gespeichert ist (Offset).
697Es soll eine ID3TagReaderException geworfen werden, wenn
698in einem Frame des Inhaltstyps URL eine invalide URL hinterlegt ist,
699ein das Encoding definierendes Byte nicht den erlaubten Werten entspricht oder
700das ein BOM ungültige Werte hat.
701public static ID3Tag read(Path path) throws ID3TagReaderException
702Liest aus der durch den übergebenen Pfad referenzierten Datei den ID3Tag aus.
703Lesen Sie dabei die Datei in einen Byte-Array ein, damit Sie die zuvor implementierten Methoden verwenden können.
704Enthält die Mediendatei einen ID3Tag, der nicht der Version v2.4.0 entspricht, soll ein ID3Tag ohne Frames zurückgegeben werden.
705Kann unter dem angegebenen Pfad keine Datei gefunden werden oder wirft eine beim Aufruf dieser Methode verwendeten, zuvor implementierten Methoden eine ID3TagReaderException, soll eine ID3TagReaderException geworfen werden.
706Hinweis zur Implementierung: Durchlaufen Sie den aus der Datei ausgelesenen Byte-Array mittels wiederholten Aufrufs der Methode readFrame. Beenden Sie die Iteration sobald die Größe des ID3Tags ausgeschöpft ist oder die 4 auf einen ausgelesenen (oder übersprungenen) Frame folgenden Bytes jeweils 0x00 sind.
707Erzeugen Sie die ID3TagReaderExceptions stets mit einer sinnvollen Fehlermeldung!
708
7092.3.2 Die Klasse ID3TagToMetadataConverter
710In dieser Klasse soll die Funktionalität zur Konvertierung eines ID3Tag-Objekts in ein MetaData-Objekt implementiert werden.
711Es wird neben den Konstruktoren nur eine weitere Methode gefordert. Es wird jedoch dringend empfohlen, weitere private Methoden zu erstellen, um den Code zu strukturieren und lesbar zu halten.
712
713public ID3TagToMetadataConverter()
714Erzeugt einen ID3TagToMetadataConverter mit einem GenreManger ohne Genres.
715public ID3TagToMetadataConverter(Collection<Genre> genres)
716Erzeugt einen ID3TagToMetadataConverter mit einem GenreManger mit den übergebenen Genres.
717public Metadata convert(ID3Tag tag)
718Gibt ein Metadata-Objekt zurück, das die Daten aus dem übergebenen ID3Tag beinhaltet.
719Dabei gilt, dass das resultierende Metadata-Objekt mit der entsprechenden get-Methode genau dann ein nicht leeres Optional zurückgeben soll, wenn der entsprechende Frame im ID3Tag vorhanden und im Falle von Frames mit dem Inhaltstyp Text nicht leer war. War der Frame nicht vorhanden, soll jeweils ein leeres Optional zurückgegeben werden.
720Die Zuordnung ist dabei wie folgt:
721 Frame-Typ -> Metadata-getter
722 TIT2 -> getSongTitle()
723 TPE1 -> getArtist()
724 TALB -> getAlbumTitle()
725 TCON -> getGenre()
726 TDRC -> getDate()
727 TRCK -> getTrackNumber()
728 WCOP -> getCopyrightInformation()
729 WPUB -> getPublisherWebpage()
730 APIC -> getPicture()
731
732Dabei sollen folgende Konventionen gelten:
733Bei einem mehrfachen Auftreten eines Frame-Typs im ID3Tag wird der jeweils erste Frame zur Konvertierung verwendet.
734Für Frames des Typs APIC gilt:
735Es soll der erste Frame verwendet werden, der den Bild-Typ 3 hat. Sollte es kein APIC-Frame mit diesem Bild-Typ geben, soll der erste APIC-Frame verwendet werden.
736Der Frame wird in ein MetadataPicture mit den entsprechenden Werten konvertiert.
737Bei der Konvertierung eines Frames des Typs TCON ist der im Konstruktor übergebene GenreManager wie folgt zu verwenden:
738Ist der Text des Frames in eine Ganzzahl (int) wandelbar und existiert im GenreManager ein Genre mit dieser ID, soll dieses verwendet werden.
739Existiert im GenreManager kein Genre mit dieser ID, soll ein neues Genre mit dieser ID und dem Namen "unknown Genre <ID>"" erstellt werden, also z. B. "unknown Genre 5".
740Ist der Text des Frames nicht in eine Ganzzahl wandelbar, wird der Text als Name des Genres interpretiert. Existiert im GenreManager ein Genre mit diesem Namen, soll dieses Genre verwendet werden.
741Existiert kein Genre mit diesem Namen, soll ein neues Genre mit der nächsten freien ID im GenreManager erstellt und verwendet werden.
742Falls der Text des Frames des Typs TRCK nicht in eine positive Ganzzahl (int) umgewandelt werden kann, soll 0 verwendet werden.
743Für Frames des Typs TDRC gilt, dass je nach Format des Textes eine bestimmte Implementierung bzw. ein bestimmter Typ von Temporal verwendet werden soll:
744Format -> Typ
745yyyy -> Year
746yyyy-MM -> YearMonth
747yyyy-MM-dd -> LocalDate
748yyyy-MM-ddTHH:mm:ss -> LocalDateTime
749
750Entspricht der Text keinem dieser Formate, soll kein Datum verwendet werden.
7512.4 Klassen zum Importieren von Musikstücken aus dem Dateisystem
752Für die in Teilaufgabe 4 zu implementierende GUI sollen nun Klassen bereitgestellt werden, mit welchen die Funktionalität zum komfortablen Laden von MP3-Dateian aus dem Dateisystem inklusive Fehlerrückmeldung zur Verfügung gestellt werden kann.
753Implementieren Sie die Klassen im Paket jpp.wuetunes.io.files.
754
7552.4.1 Die Klasse SongFileImportFailure
756Diese Klasse modelliert ein nicht erfolgreichen Einleseversuch einer Datei.
757Implementieren Sie den folgenden Konstruktor und die folgenden Methoden:
758
759public SongFileImportFailure(Path filePath, String message)
760Erzeugt einen SongFileImportFailure, der das fehlerhafte Einlesen der durch den übergebenen Pfad referenzierten Datei mit der übergebenen Nachricht darstellt.
761Hat die Nachricht die Länge 0, soll eine java.lag.IllegalArgumentException geworfen werden.
762public Path getFilePath()
763Gibt den Pfad zurück.
764public String getMessage()
765Gibt die Nachricht zurück.
766Überschreiben Sie die Methoden public boolean equals(Object obj) und public int hashCode() so, dass zwei SongFileImportFailures genau dann gleich sind, wenn deren Pfade und Nachrichten gleich sind.
767
7682.4.2 Die Klasse SongsFileImportResult
769Diese Klasse modelliert das Ergebnis eines Imports von Dateien.
770Implementieren Sie den folgenden Konstruktor und die folgenden Methoden:
771
772public SongsFileImportResult(Set<Song> songs, Set<SongFileImportFailure> failures)
773Erstellt ein SongsFileImportResult mit den übergebenen erfolgreich eingelesenen Musikstücken und nicht erfolgreichen Einleseversuchen.
774public Set<Song> getSongs()
775Gibt die Menge der eingelesenen Musikstücke zurück.
776public Set<SongFileImportFailure> getFailures()
777Gibt die Menge der nicht erfolgreichen Einleseversuchen zurück.
7782.4.3 Die Klasse SongsFileImporter
779Dies ist die Hauptklasse zum Importieren von MP3-Dateien aus dem Dateisystem. Sie sollte dazu einen ID3TagToMetadataConverter verwenden.
780Implementieren Sie den folgenden Konstruktor und die folgende Methode:
781
782public SongsFileImporter(Collection<Genre> genres)
783Erzeugt einen SongsFileImporter, dessen ID3TagToMetadataConverter die übergebenen Genres übergeben werden.
784public SongsFileImportResult importSongsFromFolder(Path folderpath) throws IOException
785Importiert alle Musikstücke, die im übergebenen Pfad gespeichert sind. Dabei sollen auch Unterverzeichnisse rekursiv einbezogen werden. Es sollen nur solche Dateien berücksichtigt werden, deren Name auf ".mp3" endet.
786Wirft der zum Importieren verwendete ID3TagToMetadataConverter eine ID3TagException, soll ein SongFileImportFailure mit deren Nachricht und dem entsprechenden Dateipfad erzeugt und über das SongsFileImportResult zurückgegeben werden.
787Die importierten Musikstücke sollen ebenfalls über das SongsFileImportResult zurückgegeben werden.
788Verweist der übergebene Pfad nicht auf ein Verzeichnis, soll eine java.lang.IllegalArgumentException geworfen werden.
789Falls während der Ausführung der Methode eine java.io.IOException wirft, soll von der Methode diese IOException geworfen werden.
790Teilaufgabe 3: Schreiben bzw. Lesen von Musikstücken in eine bzw. von einer SQLite Datenbank
791Nachdem die Musikstücke eingelesen worden sind, soll es möglich sein, diese Song Objekte in einer Datenbank zu speichern.
792Zudem soll es möglich sein die Song Objekte aus der Datenbank wieder einzulesen. Es wird eine SQLite Datenbank verwendet. Die Schreib- und Lesezugriffe sollen mit Hilfe der JDBC-API. umgesetzt werden. Dazu sei ergänzend auf die SQLite Tutorials verwiesen.
793Es empfiehlt sich, das Programm DB Browser for SQLite zu verwenden, um sich die Datenbank anzuschauen und zu debuggen.
794
795Es werden Ihnen zwei vordefinierte Datenbanken zur Verfügung gestellt:
796
797medialibrary.db: mit leeren, vordefinierten Tabellen songs und genre
798medialibrary_default_genres.db: mit leerer Tabelle songs und mit Standard-Genres gefüllte Tabelle genres
799Betrachten Sie mit Hilfe des oben genannten Tools die bereits vorhandenen Tabellen. Diese gilt es später zu füllen.
800
801Die Tabellen der Datenbank wurden mit folgender Anfrage erzeugt und bilden damit direkt die Metadaten ab.
802
803 CREATE TABLE IF NOT EXISTS songs (
804 filepath text PRIMARY KEY NOT NULL, songtitle text,
805 artist text, genreId integer, albumTitle text,
806 rating integer, playCounter integer, date text,
807 trackNumber integer, copyrightInfo text, webpage text,
808 pictureMime text, pictureDesc text, picture blob
809 );
810
811 CREATE TABLE IF NOT EXISTS genres (
812 id integer PRIMARY KEY UNIQUE NOT NULL,
813 genreName text NOT NULL
814 );
815
816Die genreId in der songs-Tabelle verweist auf die id in der genres-Tabelle und ist damit ein sogenannter Fremdschlüssel.
817
818Implementieren Sie alle Klassen im Paket jpp.wuetunes.io.database.
819
8203.1 Hilfsklassen
821Zuerst einmal sollen die benötigten Exception-Klassen implementiert werden. Bei überladenen Konstruktoren sollen keine Exceptions geworfen werden.
822Alle folgenden Exceptions erweitern IllegalStateException und sollen die folgenden Konstruktoren überschreiben:
823IllegalArgumentException(String s) und IllegalArgumentException(String message, Throwable cause).
824
8253.1.1 Die Klasse DatabaseReadException
826Diese Exception soll geworfen werden, wenn der Lesezugriff fehl schlägt.
827
8283.1.2 Die Klasse DatabaseWriteException
829Diese Exception soll geworfen werden, wenn der Schreibzugriff fehl schlägt.
830
8313.1.3 Die Klasse DatabaseOpenException
832Diese Exception soll geworfen werden, wenn das Öffnen einer Datenbank fehschlägt.
833
8343.1.4 Die Klasse InconsistentDatabaseException
835Diese Exception soll geworfen werden, wenn beim Einlesen Metadaten nicht ausgewertet werden können.
836
8373.2 Die Verbindung zur Datenbank
8383.2.1 Die Klasse DatabaseConnection
839Diese Klasse bildet die Verbindung zu Datenbank ab und stellt Methoden zum Lesen und Schreiben von Song-Objekten zur Verfügung. Diese Klasse soll das Interface java.lang.AutoClosable implementieren.
840
841public DatabaseConnection(Path path)
842Erzeugt eine DatabaseConnection mit dem übergebenen Pfad. Zu diesem Pfad soll später die Datenbankverbindung aufgebaut werden. Falls die vom Pfad referenzierte Datei nicht existiert, soll eine java.lang.IllegalArgumentException geworfen werden.
843public DatabaseConnection()
844Wenn diese Konstruktor aufgerufen wird, soll später eine Verbindung zur Beispieldatenbank im Ordner src/main/resources/database/MediaLibrary.db
845geöffnet werden.
846public String getURL()
847Gibt eine String-Repräsentation der URL zur Datenbank zurück. Die URL zur Datenbank besteht aus dem Präfix "jdbc:sqlite:" und dem im Konstruktor festgelegten Pfad.
848public String toString()
849Gibt eine String-Repräsentation der folgenden Form zurück:
850DatabaseConnection to <URL zur Datenbank>
851public boolean equals() und public int hashCode()
852Implementieren Sie diese Methoden nach den Java Richtlinien. Zwei DatabaseConnection-Objekte gelten als gleich, wenn die URLs zur Datenbank gleich sind.
853public void open()
854Öffnet eine Verbindung zur Datenbank an der durch den Pfad festgelegten URL.
855Sollte ein Fehler beim Öffnen der Datenbank auftreten, so soll eine DatabaseOpenException geworfen werden.
856Sollte die Datenbank bereits geöffnet sein, so soll eine IllegalStateException geworfen werden.
857public void close()
858In dieser Methode soll die Verbindung zur Datenbank wieder geschlossen werden.
859Sollte ein Fehler beim Schließen der Datenbank auftreten, so soll keine Exception geworfen werden.
860Die Datenbankverbindung soll auch nach einem Fehler als geschlossen gelten.
861Sollte die Verbindung bereits geschlossen sein, so soll kein Fehler geworfen werden.
862Hinweis: Stellen Sie sicher, dass diese Methode funktioniert! Dies ist ebenfalls Voraussetzung für eine erfolgreiche Überprüfung der weiteren Methoden.
863public boolean isOpen()
864Diese Methode soll true zurück geben, wenn die Verbindung zur Datenbank geöffnet ist, sonst false.
865public void writeSong(Song song)
866Schreibt das übergebene Musikstück in die die Datenbank.
867Beinhalten die Metadaten des Musikstücks ein Genre, muss dieses nach dem Aufruf der Methode in der Tabelle genre (mit ID und Name) enthalten sein.
868Für nicht vorhandene Werte aus den Metadaten gilt, dass Objekte null gesetzte werden und Typen nicht gesetzt werden.
869Falls bereits ein Musikstück mit dem gleichen Pfad in der Datenbank vorhanden ist, soll dieses überschrieben werden.
870Achten Sie darauf, dass kein Informationsverlust stattfindet. Wird ein Musikstück gespeichert und später wieder geladen, sollen alle Metadaten identisch sein. Insbesondere soll das Aufnahmedatum mit der gleiche Implementierung von Temporal gespeichert sein.
871Ist die Datenbankverbindung bereits geschlossen, soll eine IllegalStateException geworfen werden.
872Schlägt ein SQL-Statement fehl, soll eine DatabaseWriteException geworfen werden.
873public void writeSongs(Set<Song> songs)
874Schreibt die übergebenen Musikstücke in die Datenbank.
875Es gelten die gleichen Anforderungen wie bei der Methode writeSong(Song).
876public void delete()
877Sollte die Datenbankverbindung bereits geschlossen sein, so soll eine IllegalStateException geworfen werden.
878Diese Methode soll alle Inhalte der Tabellen löschen.
879Sollte ein SQL-Statement fehl schlagen soll eine DatabaseWriteException geworfen werden.
880public List<Genre> loadGenres()
881Liest die Musikstücke aus der Datenbank aus und gibt sie zurück. Die Liste soll dabei nach Id der Genres aufsteigend sortiert sein.
882Ist die Datenbankverbindung bereits geschlossen, so soll eine IllegalStateException geworfen werden.
883Schlägt ein SQL-Statement fehl, soll eine DatabaseReadException geworfen werden.
884public Set<Song> loadSongs()
885Liest die Musikstücke aus der Datenbank aus und gibt sie zurück.
886Sollte die Datenbankverbindung bereits geschlossen sein, so soll eine IllegalStateException geworfen werden.
887Sollte ein SQL-Statement fehlschlagen, soll eine DatabaseReadException geworfen werden.
888Ist eine URL falsch formatiert oder für eine genreId kein Genre in der Tabelle genres vorhanden, soll eine InconsistentDatabaseException geworfen werden.
889Teilaufgabe 4: Die JavaFX-Anwendung WueTunes
890Implementieren Sie nun eine JavaFX-Anwendung, in der die zuvor implementierten Klassen verwendet werden. Es werden keine weiteren Vorgaben zur Implementierung gemacht, außerdem finden keine automatischen Tests statt. Überprüfen Sie Ihre Implementierung selbst kritisch auf die folgenden Anforderungen!
891Lesen Sie ggf. das Oracle JavaFX Media Tutorial.
892
893Die Anwendung soll folgende Eigenschaften haben bzw. Anwendungsfälle unterstützen:
894
8954.1 Datei-Import
896Möglichkeit zum Import aller MP3-Dateien mit validem ID3-Tag eines Verzeichnisses (inklusive Unterverzeichnissen) des Dateisystens in eine Sammlung von Musikstücken. Dieses Verzeichnis soll über einen directory chooser ausgewählt werden. Es wird (beispielsweise als Alert) eine Rückmeldung gegeben, wie viele Musikstücke eingelesen wurden und bei wie vielen Musikstücken bzw. Dateien das Einlesen fehlgeschlagen ist (siehe SongFileImporterResult).
897
8984.2 Speichern in die Datenbank
899Alle importierten Musikstücke werden inklusive der Genres unmittelbar in eine an einem nicht veränderbaren Ort gespeicherte SQLite-Datenbank geschrieben. Musikstücke mit identischem Dateipfad werden überschrieben.
900
9014.3 Laden aus der Datenbank
902Enthält die Datenbank beim Start der Anwendung bereits Musikstücke, werden diese unmittelbar in die Sammlung geladen.
903
9044.4 Datei-Import bei gefüllter Datenbank
905Auch bei gefüllter Datenbank soll es möglich sein, Datein aus einem Verzeichnis zu importieren. In der Datenbank bereits vorhandene Musikstücke mit gleichem Pfad werden dabei überschrieben.
906
9074.5 Anzeige der Sammlung
908Anzeige der Musikstücke der Sammlung in tabellarischer Form mit allen Metadaten (Beim Aufnahmedatum ist die Anzeige des Jahres ausreichend). Falls in den Metadaten ein Bild vorhanden ist, soll dieses in der Tabelle als thumbnail (höchstens 50px Breite und Höhe) dargestellt werden. Die Musikstücke sollen mindestens nach Künstler, Albumtitel, Genre und Aufnahmedatum (Jahr) filter- und sortierbar sein. Standardmäßig (also z. B. beim erstmaligen Anzeigen der Sammlung) sollen die Musikstücke in natürlicher Ordnung sortiert sein.
909Falls die Tabelle sehr hoch oder breit wird, sollen Scrollbalken verwendet werden.
910
9114.6 Wiedergabeliste
912Erstellung einer Wiedergabeliste von Musikstücken der Sammlung (beispielsweise per Drag&Drop). In der Wiedergabeliste sollen Titel des Musikstücks, Künstler, Albumtitel, Nummer des Musikstücks auf dem Album und die Dauer des Musikstücks angezeigt werden.
913
9144.7 Modifikation der Wiedergabeliste
915Die Musikstücke in der Wiedergabeliste sollen gelöscht und umsortiert werden können. Ein Musikstück soll mehrfach in der Wiedergabeliste enthalten sein können.
916
9174.8 Anzeige des aktuellen Musikstücks
918Das aktuell ausgewählte Musikstück der Wiedergabeliste wird optisch hervorgehoben. Ist in den Metadaten ein Bild hinterlegt, wird dieses zusammen mit dessen Beschreibung in größerer Form (mind. 200px Breite oder Höhe) an geeigneter Position angezeigt.
919
9204.9 Bewertung von Musikstücken
921Ein Musikstück kann mit einer Bewertung zwischen 0 (unbekannt) und 5 versehen werden. Die Bewertung muss mindestens in der taballarischen Ansicht der Sammlung angezeigt werden und auch über Neustarts der Anwendung hinweg gespeichert bleiben.
922
9234.10 Wiedergabe von Musikstücken
924Bei Anzeige der Wiedergabeliste werden mit gängigen Symbolen versehene Schaltflächen mit der angegebenen Funktionalität zur Verfügung gestellt:
925
926Start/Pause: Startet die Wiedergabe des aktuellen Musikstücks, falls es gerade nicht abgespielt wird. Andernfalls wird die Wiedergabe pausiert, so dass sie bei erneutem Starten an dieser Stelle startet.
927Stop: Beendet die Wiedergabe, so dass sie bei erneutem Starten beim Beginn des Musikstücks startet.
928Zurück: Falls das aktuelle Musikstück schon mehr als etwa 3 Sekunden läuft, wird dieses von vorne abgespielt. Ansonsten wird das vorherige Musikstück in der Wiedergabeliste ausgewählt
929Vor: Das nächste Musikstück aus der Wiedergabeliste wird ausgewählt.
930Zufallsmodus: Bei Zurück/Vor wird ein zufälliges Musikstück aus der Wiedergabeliste gewählt.
931Lautstärke: Die Lautstärke der Wiedergabe soll in gängiger Weise (z.B. über einen Slider) angepasst werden können. Änderungen sollen sofort (nicht erst ab dem nächsten Musikstück) in Kraft treten.
9324.11 Darstellung des Wiedergabefortschritts
933Der Fortschritt der Wiedergabe wird durch einen Fortschrittsbalken optisch dargestellt. Über diesen Balken soll auch eine Manipulation des Wiedergabefortschritts möglich sein, also das Springen zu einem anderen Zeitpunkt im Musikstück.
934
9354.12 Wiedergabezähler
936Wird die Wiedergabe eines Musikstücks gestartet, wird ein Zähler, der angibt, wie oft das Musikstück wiedergegeben wurde, um 1 hochgezählt. Dieser play counter muss mindestens in der taballarischen Ansicht der Sammlung angezeigt werden und auch über Neustarts der Anwendung hinweg gespeichert bleiben.
937
9384.13 Ausnahmebehandlung und Standardausgabe
939Die Standardausgabe muss leer bleiben. Dies gilt insbesondere bei auftretenden Exceptions, z. B. wenn ein Musikstück abgespielt werden soll, dessen Datei nach dem Importieren gelöscht wurde oder wenn keine Datenbank-Datei vorliegt. Stattdessen soll innerhalb der Anwendung eine aussagekräftige Fehlermeldung gegeben werden (beispielsweise als Alert).
940
941Teilaufgabe 5: Song Similarities
942Diese Teilaufgabe finden Sie als separate Aufgabe in PABS.
943
944PABS 3.5 - University of Würzburg