package javacodebook.io.randomaccess; import java.io.*; /** * Jeder Eintrag im RandomAccessFile ist ein DataEntry, der selbst weiss, * in welchem Format seine Daten gespeichert und gelesen werden sollen. * Dazu werden die Methoden writeData() und readData() verwendet, die von * den entsprechenden Methoden in der Klasse FileManager aufgerufen werden. * Um das verwendete RandomAccessFile-Objekt vor unbeabsichtigtem Schliessen * oder Positionsänderungen zu schützen, werden nur die Schnittstellen * DataOutput und DataInput verwendet, die die eigentlichen Schreibmethoden * enthalten. Damit kann ein DataEntry alle relevanten Operationen ausführen. */ public interface DataEntry extends Cloneable{ public void writeData(DataOutput out) throws IOException; public void readData(DataInput in) throws IOException; public String getSearchKey(); public int getMaxSize(); public Object clone(); public void clear(); } --- Neue Klasse --- package javacodebook.io.randomaccess; import java.io.*; /** * Die Klasse FileManager ist die Schnittstelle zum RandomAccessFile. * Sie ermöglicht das hinzufügen, auffinden und löschen einzelner * Einträge und kann ein Array mit allen vorhandenen Datensätzen * erstellen. */ public class FileManager { private RandomAccessFile raf = null; private int entrySize; DataEntry entryType; /* * Erzeugt einen neuen FileManager, der die angegebene Datei als Datendatei * verwendet. * Als weiterer Parameter wird eine Instanz des verwendeten DataEntry-Typs * (also in unserem Fall: Ein Person-Objekt) übergeben. Dieses Objekt dient * als Vorlage für alle in dieser Klasse benötigten DataEntry-Objekte. */ public FileManager(String fileName, DataEntry entryType) throws IOException{ raf = new RandomAccessFile(fileName, "rw"); this.entrySize = entryType.getMaxSize(); this.entryType = (DataEntry)entryType.clone(); this.entryType.clear(); } public void addEntry(DataEntry entry) throws IOException { if(findEntry(entry.getSearchKey()) != null) throw new IOException("Eintrag ist bereits vorhanden!"); else { raf.seek(raf.length()); raf.setLength(raf.length() + entrySize); entry.writeData(raf); } } public DataEntry findEntry(String searchStr) throws IOException { //einen Dummy zum Lesen der Daten erzeugen DataEntry bufferEntry = (DataEntry)entryType.clone(); //Suche am Anfang der Datei starten raf.seek(0); int entryNr = 0; boolean found = false; while(entryNr * entrySize < raf.length() -1) { bufferEntry.clear(); bufferEntry.readData(raf); String value = bufferEntry.getSearchKey(); //Wenn der gefundene Eintrag mit dem Suchstring übereinstimmt, //wird die Schleife beendet if(value.equals(searchStr)) { found = true; break; } entryNr++; //An die Anfangsposition des nächsten Eintrags springen raf.seek(entryNr * entrySize); } if(!found) return null; else return bufferEntry; } public void deleteEntry(DataEntry entry) throws IOException { if(!(findEntry(entry.getSearchKey()) != null)) throw new IOException("Eintrag ist nicht vorhanden!"); else { //Nach dem Finden und lesen des Eintrags wieder an seine //Anfangsposition springen. Dazu Ganzahl-Division ausnutzen, sie //liefert die Nummer des Eintrags und wird wiederum multipliziert //mit der max. Länge eines Eintrags. int entryNr = (int)raf.getFilePointer()/entrySize; raf.seek(entryNr * entrySize); //Eintrag löschen, indem er mit Nullbytes überschrieben wird. byte[] emptyBytes = new byte[entrySize]; raf.write(emptyBytes); //wenn der gelöschte Eintrag nicht der letzte war, wird der letzte //Eintrag in die entstandene Lücke geschoben und die Datei verkürzt if(!(raf.getFilePointer() >= raf.length() - 1)) { int lastEntryNr = (int)raf.length()/entrySize - 1; moveEntry(lastEntryNr, entryNr); } //jetzt wird die Datei um einen Eintrag verkleinert raf.setLength(raf.length() - entrySize); } } public DataEntry[] getAllEntries() throws IOException { //Ein Array mit der benötigten Länge erzeugen DataEntry[] allEntries = new DataEntry[(int)(raf.length() + 1) / entrySize]; //einen Dummy zum Lesen der Daten erzeugen DataEntry bufferEntry = (DataEntry)entryType.clone(); //Suche am Anfang der Datei starten raf.seek(0); int entryNr = 0; while(entryNr * entrySize < raf.length() -1) { bufferEntry.clear(); bufferEntry.readData(raf); //Einen Klon des bufferEntry-Objekts im Array ablegen allEntries[entryNr] = (DataEntry)bufferEntry.clone(); entryNr++; //An die Anfangsposition des nächsten Eintrags springen raf.seek(entryNr * entrySize); } return allEntries; } public long getSize() throws IOException { return raf.length() / entrySize; } protected void finalize() throws IOException { raf.close(); } /** * Wird ein Eintrag gelöscht und er war nicht der letzte am Ende, * so wird der letzte Eintrag in die entstandene Lücke verschoben und * die Datei wird verkürzt. */ private void moveEntry(int sourceIndex, int targetIndex) throws IOException { //zu verschiebenden Eintrag lesen raf.seek(entrySize * sourceIndex); byte[] buffer = new byte[entrySize]; raf.readFully(buffer); //an die Zielstelle schreiben raf.seek(entrySize * targetIndex); raf.write(buffer); //alte stelle löschen raf.seek(entrySize * sourceIndex); byte[] emptyBytes = new byte[entrySize]; raf.write(emptyBytes); } } --- Neue Klasse --- package javacodebook.io.randomaccess; import java.io.*; /** * Die Klasse Person ist die Datenklasse. Eine Person hat die Daten * Name, Alter, Adresse. */ public class Person implements DataEntry, Comparable { //Bestimmt die max. Anzahl Bytes, die ein Datensatz belegen kann public static final int SIZE = 200; private String name; private int alter; private String adresse; public Person() { } public Person(String name, int alter, String adresse) { this.name = name; this.alter = alter; this.adresse = adresse; } public void readData(DataInput in) throws IOException { name = in.readUTF(); alter = in.readInt(); adresse = in.readUTF(); } public void writeData(DataOutput out) throws IOException { out.writeUTF(name); out.writeInt(alter); out.writeUTF(adresse); } public String toString() { return "Person: " + name + ", " + alter + " Jahre, " + adresse; } public String getSearchKey() { return name; } public int getMaxSize() { return SIZE; } public void clear() { name = ""; alter = 0; adresse = ""; } public String getName() { return name; } public int getAlter() { return alter; } public String getAdresse() { return adresse; } public void setName(String name) { this.name = name; } public void setAlter(int alter) { this.alter = alter; } public void setAdresse(String adresse) { this.adresse = adresse; } public Object clone() { return new Person(name, alter, adresse); } /** * Erlaubt den Vergleich mit einer anderen Person über den Namen. * Wird für die Sortierung benötigt. */ public int compareTo(Object obj) { if(!(obj instanceof Person)) throw new ClassCastException("Vergleichsobjekt ist keine Person"); return name.compareTo(((Person)obj).getName()); } } --- Neue Klasse --- package javacodebook.io.randomaccess; import java.io.*; /** * Die Klasse PersonDatabase steuert den Zugriff auf den FileManager und * stellt die Methoden zum Hinzufügen, Finden und Löschen von Personen * sowie zur Auflistung aller Datensätze zur Verfügung. */ public class PersonDatabase { private FileManager fileManager; BufferedReader in = null; /** * Erzeugt eine neue PersonDatenbank. Als Parameter wird der Dateiname * angegeben, unter dem die Daten gespeichert werden sollen. */ public PersonDatabase(String dataFile) { try { fileManager = new FileManager(dataFile, new Person()); //Eingabestrom zum Einlesen der Werte anlegen InputStreamReader reader = new InputStreamReader(System.in); in = new BufferedReader(reader); } catch(IOException e) { System.out.println("Konnte Datei nicht öffnen"); e.printStackTrace(System.out); } } public void addPerson() { try { Person p = new Person(); System.out.print("Name: "); p.setName(in.readLine()); System.out.print("Alter: "); p.setAlter(Integer.parseInt(in.readLine())); System.out.print("Adresse: "); p.setAdresse(in.readLine()); fileManager.addEntry(p); } catch(IOException e) { System.out.println("Konnte Datensatz nicht hinzufügen"); e.printStackTrace(System.out); } } public Person findPerson() { Person p = null; try { System.out.print("Name: "); String name = in.readLine(); p = (Person)fileManager.findEntry(name); if(p != null) { System.out.println(p.toString()); } else { System.out.println(name + " nicht gefunden"); } } catch(IOException e) { System.out.println("Konnte Datensatz nicht finden"); e.printStackTrace(System.out); } return p; } public void deletePerson() { try { Person p = findPerson(); if(p != null) { fileManager.deleteEntry(p); System.out.println(p.getName() + " wurde gelöscht"); } } catch(IOException e) { System.out.println("Konnte Datensatz nicht finden"); e.printStackTrace(System.out); } } public void showAll() { try { DataEntry[] personen = fileManager.getAllEntries(); java.util.Arrays.sort(personen); for(int i = 0; i < personen.length; i++) System.out.println(((DataEntry)personen[i]).toString()); } catch(IOException e) { System.out.println("Konnte Datensatz nicht finden"); e.printStackTrace(System.out); } } public static void main(String[] args) throws IOException { if(args.length < 1) { printUsage(); System.exit(0); } String fileName = args[0]; PersonDatabase database = new PersonDatabase(fileName); try { //Schleife über das Kommandozeilen-Menü boolean repeat = true; while(repeat) { System.out.println("Bitte wählen Sie eine Funktion:"); System.out.print("(h)inzufügen (s)uchen (l)öschen "); System.out.print("(a)lle Datensätze anzeigen "); System.out.println("b(e)enden"); InputStreamReader reader = new InputStreamReader(System.in); BufferedReader in = new BufferedReader(reader); char c = (char)in.read(); switch(c) { case 'h': System.out.println("Neuer Datensatz:"); database.addPerson(); break; case 's': System.out.println("Datensatz suchen:"); database.findPerson(); break; case 'l': System.out.println("Datensatz löschen"); database.deletePerson(); break; case 'a': System.out.println("Alle Datensätze:"); database.showAll(); break; case 'e': System.out.println("Programm wird beendet"); repeat = false; break; default: System.out.println("Unbekannter Befehl " + c); break; } } } catch(Exception e) { e.printStackTrace(System.out); } } private static void printUsage() { System.out.println("Benutzung: java javacodebook.io.randomacces.PersonDatabase dateiname"); } }