Aufgabe 1

Lösungsidee

Wie gefordert wurde die Aufgabe als lineare Liste implementiert. Die Methode append(Entry) fügt einen Eintrag am Ende der Liste, die Methode prepend(Entry) am Anfang der Liste an. Diese beiden Methoden sind als private deklariert und werden nur (bei Bedarf) in der Methode insert(Entry) aufgerufen, welche wiederum als public deklariert ist und zum (sortierten) Einfügen eines neuen Telefonbucheintrags vom rufenden Prg. genutzt werden kann. Die Liste enthält zusätzlich einen Verweis auf den letzten Eintrag der Liste, damit in append(Entry) beim Anfügen eines Eintrags am Ende der Liste nicht erst die ganze Liste durchlaufen werden muss. Die Methode insert(Entry) sorgt auch dafür, dass das Telefonbuch immer sortiert ist und bleibt.

Entry lookupEntry(String) ermöglicht das Suchen eines bestimmten Namen im Telefonbuch, sie nutzt die Methode Entry.compareNameTo(Entry), wobei mit dem zu suchenden Namen ein temporäres Entry-Objekt erzeugt wird. Dies bewerkstelligte, dass der Code zum Vergleichen von Namen (einmal zum Suchen, einmal in insert(Entry) für die Sortierung) nur einmal existiert (und implementiert werden musste). Die möglichen Ergebnisse dieses Vergleichs wurden in static final Variablen (Entry.SMALLER, Entry.GREATEREntry.EQUAL) gespeichert und können vom rufenden Prg. zur Vermeidung von hardcodierten int-Werten verwendet werden
Wird der Suchname nicht vollständig angeg., wird der (erste) diesem Suchnamen am nächsten kommende Eintrag zurückgeg. (wie in einem richtigen Handy-Telefonbuch). Existiert beispielsweise ein Eintrag mit Namen "Daniel Brunthaler" als erster Eintrag mit den Anfangsbuchstaben "Da" und ist der Suchname = "Da", wird dieser Name zurückgeg.

Der Source enthält der Einfachheit halber (um den fast identischen Source nicht zweimal anführen zu müssen) auch die wenigen Erweiterungen für Aufgabe 2. Erläuterungen dazu und der Source für den PhoneBookIndex folgen in der Lösungsidee und dem Source zu Aufgabe 2.

Source

/**
 * Softwareentwicklung I - Exercise 10/1.
 * Phone book.
 * In-source comments are followed by the described code.
 * 
 * @see Entry, PhoneBookIndex.
 * @author Daniel Brunthaler (0255054)
 */
public class PhoneBook {
    
    private Entry firstEntry = null;
    private Entry lastEntry = null;
    private PhoneBookIndex phoneBookIndex = new PhoneBookIndex();
    
    /**
     * Constructs an empty PhoneBook.
     */
    public PhoneBook() {
        super();
    }

    public Entry getFirstEntry() {
        return firstEntry;
    }
    
    /**
     * Prepend the given Entry.
     * @param e Entry which should be prepended.
     */
    private void prepend(Entry e) {
        if (firstEntry == null) {    // empty PhoneBook => first Entry in list.
            firstEntry = e;
            lastEntry = e;
        } else {
            e.setNext(firstEntry);
            firstEntry = e;
        }
        phoneBookIndex.add(e);
    }
    
    /**
     * Append the given Entry.
     * @param e Entry which should be appended.
     */
    private void append(Entry e) {
        e.setNext(null);           // last Entry => no next Entry.
        if (firstEntry == null) {  // empty PoneBook => first Entry in list.
            firstEntry = e;
            lastEntry = e;
            phoneBookIndex.add(e);
        } else {
            lastEntry.setNext(e);   // append given node.
            if (lastEntry.getName().charAt(0) != e.getName().charAt(0)) 
                phoneBookIndex.add(e);
            lastEntry = e;
        }
    }
    
    /**
     * Insert given Entry in the right place of (name) sorted PhoneBook that 
     * the PhoneBook remains (name) sorted.
     * @param e Entry which should be inserted.
     */
    public void insert(Entry e) {
        Entry before = null;
        Entry after = firstEntry;
        while (after != null) {
            if (e.compareNameTo(after) == Entry.SMALLER) {
                if (before == null) {
                    // Entry is smaller than first Entry of PhoneBook
                    // => prepend Entry.
                    prepend(e);
                } else {
                    before.setNext(e);
                    e.setNext(after);
                    if (before.getName().charAt(0) != e.getName().charAt(0))
                        phoneBookIndex.add(e);
                }
                return;
            }
            before = after;
            after = after.getNext();
        }
        // Entry is bigger than last Entry of PhoneBook => append Entry.
        append(e);
    }
    
    /**
     * Search for given name in the PhoneBook. Returns the first Entry, which 
     * name match the given name (at least beginning of the name). If no 
     * matching name is found, null is returned.
     * @param name which should be searched.
     * @return Entry which name match the given name.
     */
    public Entry lookupEntry(String name) {
        Entry temp = new Entry(name,null);
        Entry e = firstEntry;
        Entry before = null;
        while (e != null) {
            int compareResult = temp.compareNameTo(e);
            if (compareResult == Entry.EQUAL) {
                return e;
            }
            e = e.getNext();
        }
        return null;
    }
    
    /**
     * First Entry which first character of name matches the given character.
     * If Entry not exists, the first existing Entry, which name consists of 
     * a following character is returned. Null if no following entries exist.
     * @param sign first character 
     * @return Entry which first character of name matches the given character.
     */
    public Entry firstEntryWith(char sign) {
        return phoneBookIndex.firstEntryWith(sign);
    }
        
    /**
     * Test application for PhoneBook.
     * @param args
     */
    public static void main(String[] args) {
        PhoneBook phoneBook = new PhoneBook();
        Entry next = null, searchResult = null;
        String prompt = "Bitte Operation auswählen (i,f,n,s,c,t,q): ";
        String name, telNumber;
        char input, sign;
        IO.writeLn("Telefonbuch");
        IO.writeLn("===========\n");
        IO.writeLn("Folgende Operationen stehen zur Verfügung:");
        IO.writeLn("  i - insert: Einfügen eines Eintrags");
        IO.writeLn("  f - first: Ersten Eintrag anzeigen");
        IO.writeLn("  n - next: Nächsten Eintrag anzeigen");
        IO.writeLn("  s - search: Einen Eintrag nach Namen nachschlagen");
        IO.writeLn("  c - character: Ersten Eintrag mit bestimmten "+
                    "Buchstaben anzeigen");
        IO.writeLn("  t - test: Testeinträge automatisch erzeugen");
        IO.writeLn("  q - quit: Programm verlassen\n");
        IO.write(prompt);
        input = IO.readLine().trim().toLowerCase().charAt(0);
        while (input != 'q') {
            switch (input) {
                case 'i': 
                    IO.write("   Name? ");
                    name = IO.readLine();
                    IO.write("   Telefonnummer? ");
                    telNumber = IO.readLine();
                    if (name.trim().length() > 0)
                        phoneBook.insert(new Entry(name,telNumber));
                    else
                        IO.writeLn("   Name ungültig!");
                    break;
                case 'f':
                    if (phoneBook.getFirstEntry() == null) {
                        IO.writeLn("   Telefonbuch leer!");
                    } else {
                        IO.writeLn("--> " + 
                            phoneBook.getFirstEntry().toString());
                        next = phoneBook.getFirstEntry().getNext();
                    }
                    break;
                case 'n':
                    if (next == null) {
                        IO.writeLn("   Ende des Telefonbuchs erreicht!");
                    } else {
                        IO.writeLn("--> " + next.toString());
                        next = next.getNext();
                    }
                    break;
                case 's':
                    IO.write("   Name? ");
                    name = IO.readLine().trim();
                    searchResult = phoneBook.lookupEntry(name);
                    if (searchResult == null) {
                        IO.writeLn("   Keine passenden Einträge gefunden!");
                    } else {
                        IO.writeLn("--> " + searchResult.toString());
                    }
                    break;
                case 'c':
                    if (phoneBook.getFirstEntry() == null) {
                        IO.writeLn("   Telefonbuch leer!");
                    } else {                    
                        IO.write("   Buchstabe? ");
                        sign = IO.readLine().trim().toLowerCase().charAt(0);
                        searchResult = phoneBook.firstEntryWith(sign);
                        if (searchResult == null) {
                            IO.writeLn("   Keine passenden Einträge gefunden!");
                        } else {
                            IO.writeLn("--> " + searchResult.toString());
                        }
                    }
                    break;
                case 't':
                    phoneBook.insert(new Entry(
                        "Anton Muster","+43 (732) 1111"));
                    phoneBook.insert(new Entry(
                        "Bertold Brecht","+43 (6333) 2222"));
                    phoneBook.insert(new Entry("Daniel","+43 650 31200000"));
                    phoneBook.insert(new Entry(
                        "Daniel Home","+43 (6225) 3210"));
                    phoneBook.insert(new Entry("Hubert","12345678"));
                    phoneBook.insert(new Entry("Zenzi","+43 664 54321"));
                    IO.writeLn("   Testeinträge erzeugt");
                    break;
                default:
                    IO.writeLn("   Unbekanntes Kommando!");
                    break;
            }
            IO.write("\n" + prompt);
            input = IO.readLine().trim().toLowerCase().charAt(0);
        }
    }
}

/** * Softwareentwicklung I - Exercise 10/1. * Entry for PhoneBook. * In-source comments are followed by the described code. * * @see PhoneBook. * @author Daniel Brunthaler (0255054) */ public class Entry { public static final int SMALLER = -1; public static final int EQUAL = 0; public static final int GREATER = 1; private Entry next = null; private String name = null; private String telNumber = null; /** * Constructs an Entry. * @param name of contact. * @param telNumber telephone number of contact. */ public Entry(String name,String telNumber) { super(); this.name = name.trim(); if (telNumber == null) this.telNumber = "not entered"; else this.telNumber = telNumber.trim(); } /** * Get Entry following this Entry. * @return next Entry following this Entry. */ public Entry getNext() { return this.next; } /** * Set following Entry. * @param e Entry which should be set as following Entry. */ public void setNext(Entry e) { this.next = e; } /** * Get name of this Entry. * @return name of Entry. */ public String getName() { return this.name; } /** * Telephone number of this Entry. * @return telephone number of Entry. */ public String getTelNumber() { return this.telNumber; } /** * Compare name of this Entry to the name of another Entry. * @param e Entry which should this Entry compared with. * @return Entry.SMALLER, Entry.GREATER or Entry.EQUAL. */ public int compareNameTo(Entry e) { String name1 = this.getName().toLowerCase(); String name2 = e.getName().toLowerCase(); int length = name1.length(); int intValue1,intValue2; if (name2.length() < length) { int numberOfSpacesToAdd = length - name2.length(); for (int i = 0;i < numberOfSpacesToAdd;i++) name2 += " "; } for (int i = 0;i < length;i++) { intValue1 = Character.getNumericValue(name1.charAt(i)); intValue2 = Character.getNumericValue(name2.charAt(i)); if (intValue1 < intValue2) { return SMALLER; } else if (intValue1 > intValue2) { return GREATER; } } return EQUAL; } /** * Adequate String of this Entry. * @return ", ". */ public String toString() { return name + ", " + telNumber; } }

Test

Der Test wurde in der main-Methode implementiert (siehe Source). Es wurde praktisch der in der Aufgabenstellung angeführte Beispieldialog realisiert. Um das Testen zu vereinfachen, wurde die Testapplikation um die Fkt. "t -Testeinträge automatisch erzeugen" erweitert. Diese Fkt. erzeugt automatisch die in der main-Methode ersichtlichen Testeinträge im Telefonbuch. Die Testapplikation enthält auch schon die Fkt. "c", welche aber erst in der Aufgabe 2 zum Einsatz kommt.

Testplan

Eingabe
Erwartete Ausgabe
Zweck
i - Eintrag "Markus ..." erzeugen
-
Einen Eintrag manuell erzeugen
f - Ersten Eintrag anzeigen
"Markus ..."
Wurde erfasster Eintrag richtig gespeichert?
t - Testeinträge erzeugen
-
Telefonbuch mit Einträgen füllen
f - Ersten Eintrag anzeigen
"Anton ..."
Wurde Telefonbuch richtig befüllt? Ist es noch sortiert?
n - Nächsten Eintrag anzeigen
"Bertold ..."
Einträge und Sortierung überprüfen
n - Nächsten Eintrag anzeigen
"Daniel ..."
Einträge und Sortierung überprüfen
...
...
...
n - Nächsten Eintrag anzeigen
Ende der Liste erreicht
Ende der Liste
s - "Da" suchen
"Daniel ..."
Eintrag suchen
s - "Daniel H" suchen
"Daniel Home ..."
Auf "Daniel" folgenden Eintrag suchen
s - "Z" suchen
"Zenzi ..."
Letzten Eintrag suchen
q - Programm beenden
-
Programm beenden

Hardcopy

test1_hardcopy

Aufgabe 2

Lösungsidee

Die Klasse PhoneBookIndex ist relativ kompakt ausgefallen. Dadurch, dass jeder Anfangsbuchstabe einfach und eindeutig einem Index im Anfangsbuchstaben-Array zugeordnet werden kann, fällt die Implementation der Methode add(Entry) sehr kurz aus. In der Klasse PhoneBook wurden einige wenige Anpassungen vorgenommen, sodass beim Erzeugen eines Telefonbuch-Eintrags immer auch ein entspr. Index im PhoneBookIndex erzeugt wird (siehe Source zu Aufgabe 1). Die Klasse Entry blieb unverändert.
Die Methode Entry firstEntryWith(char) gibt den Eintrag, wessen Name den übergebenen Anfangsbuchstaben hat, oder wenn ein solcher nicht existiert, den nächsten folgenden Namen (Eintrag) zurück. Existieren danach keine Namen (Einträge) mehr, wird null zurückgeg.

Source

/**
 * Softwareentwicklung I - Exercise 10/2.
 * PhoneBookIndex for PhoneBook.
 * In-source comments are followed by the described code.
 * 
 * @see PhoneBook.
 * @author Daniel Brunthaler (0255054)
 */
public class PhoneBookIndex {

    private Entry[] firstCharIndex = new Entry[26];
    
    /**
     * Constructs an empty PhoneBookIndex.
     */
    public PhoneBookIndex() {
        super();
    }

    /**
     * Adds an Entry to the index.
     * @param e Entry which should be added to the index.
     */
    public void add(Entry e) {
        char firstChar = Character.toLowerCase(e.getName().charAt(0));
        firstCharIndex[Character.getNumericValue(firstChar) - 10] = e;
    }
    
    /**
     * First Entry which first character of name matches the given character.
     * If Entry not exists, the first existing Entry, which name consists of 
     * a following character is returned. Null if no following entries exist.
     * @param sign first character 
     * @return Entry which first character of name matches the given character.
     */
    public Entry firstEntryWith(char sign) {
        Entry e = null;
        sign = Character.toLowerCase(sign);
        for (int i = Character.getNumericValue(sign) - 10;i < 26;i++) {
            e = firstCharIndex[i];
            if (e != null) return e;
        }
        return null;
    }
}

Test

Zum Testen wurde wieder die Fkt. "t - Testeinträge automatisch erzeugen" verwendet. Auf diesen Testeinträgen kann schließlich die Fkt. "c" angewendet werden. Vor dem Erzeugen der Testeinträge wird ein einzelner Eintrag erzeugt, um den Fall, dass kein "Folgename" existiert, zu testen.


Testplan

Eingabe
Erwartete Ausgabe
Zweck
i - Eintrag "Markus ..." erzeugen
-
Einen Eintrag manuell erzeugen
c - Erster Eintrag mit Buchstabe "m" "Markus ..."
Wird erfasster Eintrag angezeigt?
c - Erster Eintrag mit Buchstabe "n" Nicht gefunden Testfall - es existiert kein "Folgename"
t - Testeinträge erzeugen - Testeinträge erzeugen
c - Erster Eintrag mit Buchstabe "a" "Anton ..."
Wird richtiger Eintrag angezeigt?
c - Erster Eintrag mit Buchstabe "d" "Daniel ..."
Wird richtiger Eintrag angezeigt? (Diesmal exisiteren zwei Einträge mit Namen "D..."
c - Erster Eintrag mit Buchstabe "e" "Hubert ..." Wird richtiger Folgename angezeigt?
c - Erster Eintrag mit Buchstabe "n" "Zenzi ..." Wird richtiger Folgename angezeigt?
q - Programm beenden
-
Programm beenden

Hardcopy
test2_hardcopy