first commit

This commit is contained in:
Boris
2024-01-15 20:14:10 +00:00
commit 8c81ee28b7
3106 changed files with 474415 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ notebook.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ a\ key\ that\ is\ currently\ in\ use.\n\ @param\ details\ The\ replacement\ details.\ Must\ not\ be\ null.\n\ @throws\ IllegalArgumentException\ If\ either\ argument\ is\ null.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\ This\ may\ be\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ zero\ length,\ but\ must\ not\ be\ null.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ @return\ The\ number\ of\ entries\ currently\ in\ the\n\ \ \ \ \ \ \ \ \ address\ book.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ the\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ The\ key\ should\ be\ one\ that\ is\ currently\ in\ use.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n\ @throws\ IllegalArgumentException\ If\ the\ key\ is\ null.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ @return\ All\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n
comment9.params=
comment9.target=boolean\ consistentSize()
comment9.text=\n\ Check\ that\ the\ numberOfEntries\ field\ is\ consistent\ with\n\ the\ number\ of\ entries\ actually\ stored\ in\ the\ address\ book.\n\ @return\ true\ if\ the\ field\ is\ consistent,\ false\ otherwise.\n
numComments=10

View File

@@ -0,0 +1,187 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
* This version uses assert statements to check internal
* consistency.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
if(details == null) {
throw new IllegalArgumentException("Null details passed to addDetails.");
}
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
assert consistentSize() : "Inconsistent book size in addDetails";
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
This should be a key that is currently in use.
* @param details The replacement details. Must not be null.
* @throws IllegalArgumentException If either argument is null.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
if(details == null) {
throw new IllegalArgumentException("Null details passed to changeDetails.");
}
if(oldKey == null){
throw new IllegalArgumentException("Null key passed to changeDetails.");
}
if(keyInUse(oldKey)){
removeDetails(oldKey);
addDetails(details);
}
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on. This may be
* of zero length, but must not be null.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
if(keyPrefix != null) {
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
endOfSearch = true;
}
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove the entry with the given key from the address book.
* The key should be one that is currently in use.
* @param key One of the keys of the entry to be removed.
* @throws IllegalArgumentException If the key is null.
*/
public void removeDetails(String key)
{
if(key == null){
throw new IllegalArgumentException("Null key passed to removeDetails.");
}
if(keyInUse(key)) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
assert !keyInUse(key);
assert consistentSize() : "Inconsistent book size in removeDetails";
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
/**
* Check that the numberOfEntries field is consistent with
* the number of entries actually stored in the address book.
* @return true if the field is consistent, false otherwise.
*/
private boolean consistentSize()
{
Collection<ContactDetails> allEntries = book.values();
// Eliminate duplicates as we are using multiple keys.
Set<ContactDetails> uniqueEntries = new HashSet<>(allEntries);
int actualCount = uniqueEntries.size();
return numberOfEntries == actualCount;
}
}

View File

@@ -0,0 +1,32 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBookDemo()
comment0.text=\n\ Setup\ some\ sample\ data.\n
comment1.params=
comment1.target=void\ setup()
comment1.text=\n\ Setup\ a\ new\ AddressBook\ with\ sample\ data.\n
comment2.params=
comment2.target=void\ testAddition()
comment2.text=\n\ A\ simple\ test\ of\ addDetails\ to\ see\ if\ an\ assertion\ fails.\n
comment3.params=
comment3.target=void\ testRemoval()
comment3.text=\n\ A\ simple\ test\ of\ removeDetails\ to\ see\ if\ an\ assertion\ fails.\n
comment4.params=
comment4.target=void\ testChange()
comment4.text=\n\ A\ simple\ test\ of\ changeDetails\ to\ see\ if\ an\ assertion\ fails.\n
comment5.params=
comment5.target=void\ testForAdditionError()
comment5.text=\n\ Add\ for\ a\ second\ time\ an\ entry\ with\ duplicate\ name\n\ and\ phone\ details.\n\ This\ should\ trigger\ an\ AssertionError.\n
comment6.params=
comment6.target=AddressBook\ getBook()
comment6.text=\n\ @return\ The\ sample\ address\ book.\n
comment7.params=
comment7.target=ContactDetails\ getExistingContact()
comment7.text=\n\ @return\ The\ details\ of\ a\ contact\ who\ is\ originally\ in\n\ \ \ \ \ \ \ \ \ the\ address\ book.\n
comment8.params=
comment8.target=ContactDetails\ getFurtherContact()
comment8.text=\n\ @return\ The\ details\ of\ a\ further\ contact,\ not\ originally\n\ \ \ \ \ \ \ \ \ in\ the\ address\ book.\n
comment9.params=
comment9.target=ContactDetails\ createRevisedDetails()
comment9.text=\n\ Create\ a\ contact\ based\ on\ an\ existing\ contact.\n
numComments=10

View File

@@ -0,0 +1,133 @@
/**
* A demo class for AddressBook.
* Some basic test methods are included. The purpose of these
* is to see whether assertion errors are triggered for some
* of the basic operations on an address book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookDemo
{
// Sample contact details.
private ContactDetails[] sampleDetails;
// An address book containing sample data.
private AddressBook book;
// A copy of one of the contact's details.
private ContactDetails existingContact;
// An additional contact.
private ContactDetails furtherContact;
/**
* Setup some sample data.
*/
public AddressBookDemo()
{
// A sample of contact details to be stored in the book.
sampleDetails = new ContactDetails[] {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
// Take a copy of the details of one of the contacts.
ContactDetails first = sampleDetails[0];
existingContact = new ContactDetails(first.getName(), first.getPhone(),
first.getAddress());
// Create a further contact who is not yet in the address book.
furtherContact = new ContactDetails("ian", "08459 900000", "address 9");
}
/**
* Setup a new AddressBook with sample data.
*/
public void setup()
{
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
}
/**
* A simple test of addDetails to see if an assertion fails.
*/
public void testAddition()
{
setup();
book.addDetails(furtherContact);
}
/**
* A simple test of removeDetails to see if an assertion fails.
*/
public void testRemoval()
{
setup();
book.removeDetails(existingContact.getName());
}
/**
* A simple test of changeDetails to see if an assertion fails.
*/
public void testChange()
{
setup();
ContactDetails revisedDetails = createRevisedDetails();
book.changeDetails(existingContact.getName(),
revisedDetails);
}
/**
* Add for a second time an entry with duplicate name
* and phone details.
* This should trigger an AssertionError.
*/
public void testForAdditionError()
{
setup();
book.addDetails(createRevisedDetails());
}
/**
* @return The sample address book.
*/
public AddressBook getBook()
{
return book;
}
/**
* @return The details of a contact who is originally in
* the address book.
*/
public ContactDetails getExistingContact()
{
return existingContact;
}
/**
* @return The details of a further contact, not originally
* in the address book.
*/
public ContactDetails getFurtherContact()
{
return furtherContact;
}
/**
* Create a contact based on an existing contact.
*/
private ContactDetails createRevisedDetails()
{
return new ContactDetails(existingContact.getName(),
existingContact.getPhone(),
existingContact.getAddress() + "X");
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n\ @throws\ IllegalStateException\ If\ both\ name\ and\ phone\ are\ blank.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=otherDetails
comment5.target=int\ compareTo(ContactDetails)
comment5.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment6.params=
comment6.target=java.lang.String\ toString()
comment6.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,129 @@
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
* @throws IllegalStateException If both name and phone are blank.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
if(this.name.isEmpty() && this.phone.isEmpty()) {
throw new IllegalStateException(
"Either the name or phone must not be blank.");
}
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,15 @@
Project: address-book-assert
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple address-book implementation.
How to start this project:
Create an AddressBookDemo object and then use its
test methods to check for assertion errors.

View File

@@ -0,0 +1,64 @@
#BlueJ package file
dependency1.from=AddressBook
dependency1.to=ContactDetails
dependency1.type=UsesDependency
dependency2.from=AddressBookDemo
dependency2.to=ContactDetails
dependency2.type=UsesDependency
dependency3.from=AddressBookDemo
dependency3.to=AddressBook
dependency3.type=UsesDependency
objectbench.height=76
objectbench.width=842
package.editor.height=458
package.editor.width=734
package.editor.x=70
package.editor.y=80
package.numDependencies=3
package.numTargets=3
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=559
readme.editor.width=784
readme.editor.x=53
readme.editor.y=23
target1.editor.height=742
target1.editor.width=883
target1.editor.x=145
target1.editor.y=58
target1.height=60
target1.name=AddressBook
target1.naviview.expanded=true
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=140
target1.x=240
target1.y=160
target2.editor.height=735
target2.editor.width=879
target2.editor.x=53
target2.editor.y=60
target2.height=60
target2.name=ContactDetails
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=130
target2.x=400
target2.y=260
target3.editor.height=743
target3.editor.width=853
target3.editor.x=50
target3.editor.y=57
target3.height=60
target3.name=AddressBookDemo
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=150
target3.x=90
target3.y=60

View File

@@ -0,0 +1,29 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ address\ book.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ a\ key\ that\ is\ currently\ in\ use.\n\ @param\ details\ The\ replacement\ details.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\ This\ may\ be\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ zero\ length,\ but\ must\ not\ be\ null.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ Return\ the\ number\ of\ entries\ currently\ in\ the\n\ address\ book.\n\ @return\ The\ number\ of\ entries.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ the\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ The\ key\ should\ be\ one\ that\ is\ currently\ in\ use.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ Return\ all\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n\ @return\ A\ sorted\ list\ of\ the\ details.\n
numComments=9

View File

@@ -0,0 +1,155 @@
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook implements Serializable
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
if(details != null) {
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
}
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
This should be a key that is currently in use.
* @param details The replacement details.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
if(keyInUse(oldKey) && details != null) {
removeDetails(oldKey);
addDetails(details);
}
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on. This may be
* of zero length, but must not be null.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
if(keyPrefix != null) {
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
endOfSearch = true;
}
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove the entry with the given key from the address book.
* The key should be one that is currently in use.
* @param key One of the keys of the entry to be removed.
*/
public void removeDetails(String key)
{
if(keyInUse(key)) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
}

View File

@@ -0,0 +1,8 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBookDemo()
comment0.text=\n\ Setup\ an\ AddressBook\ with\ sample\ data.\n\ The\ address\ book\ can\ be\ retrieved\ using\ the\ getBook\ method.\ \n
comment1.params=
comment1.target=AddressBook\ getBook()
comment1.text=\n\ @return\ The\ sample\ address\ book.\n
numComments=2

View File

@@ -0,0 +1,41 @@
/**
* Provide a simple demonstration of the AddressBook class.
* Sample data is added to the address book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookDemo
{
private AddressBook book;
/**
* Setup an AddressBook with sample data.
* The address book can be retrieved using the getBook method.
*/
public AddressBookDemo()
{
ContactDetails[] sampleDetails = {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
}
/**
* @return The sample address book.
*/
public AddressBook getBook()
{
return book;
}
}

View File

@@ -0,0 +1,20 @@
#BlueJ class context
comment0.params=book
comment0.target=AddressBookFileHandler(AddressBook)
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ FileHandler.\n\ @param\ book\ The\ address\ book\ to\ use.\n
comment1.params=keyPrefix
comment1.target=void\ saveSearchResults(java.lang.String)
comment1.text=\n\ Save\ the\ results\ of\ an\ address-book\ search\ to\n\ the\ file\ "results.txt"\ in\ the\ project\ folder.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\n
comment2.params=
comment2.target=void\ showSearchResults()
comment2.text=\n\ Show\ the\ results\ from\ the\ most-recent\ call\ to\n\ saveSearchResults.\ As\ output\ is\ to\ the\ console,\ any\n\ problems\ are\ reported\ directly\ by\ this\ method.\n
comment3.params=filename
comment3.target=void\ addEntriesFromFile(java.lang.String)
comment3.text=\n\ Add\ further\ entries\ to\ the\ address\ book,\ from\ a\ text\ file.\n\ The\ file\ is\ assumed\ to\ contain\ one\ element\ per\ line,\n\ plus\ a\ blank\ line,\ for\ each\ entry\:\n\ \ \ \ \ name\ \\n\ phone\ \\n\ address\ \\n\ \\n\n\ A\ line\ may\ be\ blank\ if\ that\ part\ of\ the\ details\ is\ missing.\n\ @param\ filename\ The\ text\ file\ containing\ the\ details.\n\ @throws\ IOException\ On\ input\ failure.\n
comment4.params=sourceFile
comment4.target=AddressBook\ readFromFile(java.lang.String)
comment4.text=\n\ Read\ the\ binary\ version\ of\ an\ address\ book\ from\ the\ given\ file.\n\ If\ the\ file\ name\ is\ not\ an\ absolute\ path,\ then\ it\ is\ assumed\n\ to\ be\ relative\ to\ the\ current\ project\ folder.\n\ @param\ sourceFile\ The\ file\ from\ where\ the\ details\ are\ to\ be\ read.\n\ @return\ The\ address\ book\ object.\n\ @throws\ IOException\ If\ the\ reading\ process\ fails\ for\ any\ reason.\n
comment5.params=destinationFile
comment5.target=void\ saveToFile(java.lang.String)
comment5.text=\n\ Save\ a\ binary\ version\ of\ the\ address\ book\ to\ the\ given\ file.\n\ If\ the\ file\ name\ is\ not\ an\ absolute\ path,\ then\ it\ is\ assumed\n\ to\ be\ relative\ to\ the\ current\ project\ folder.\n\ @param\ destinationFile\ The\ file\ where\ the\ details\ are\ to\ be\ saved.\n\ @throws\ IOException\ If\ the\ saving\ process\ fails\ for\ any\ reason.\n
numComments=6

View File

@@ -0,0 +1,169 @@
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.*;
/**
* Provide a range of file-handling operations on an AddressBook.
* These methods demonstrate a range of basic features of the
* java.io package.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookFileHandler
{
// The address book on which i/o operations are performed.
private AddressBook book;
// The name of a file used to store search results.
private static final String RESULTS_FILE = "results.txt";
/**
* Constructor for objects of class FileHandler.
* @param book The address book to use.
*/
public AddressBookFileHandler(AddressBook book)
{
this.book = book;
}
/**
* Save the results of an address-book search to
* the file "results.txt" in the project folder.
* @param keyPrefix The key prefix to search on.
*/
public void saveSearchResults(String keyPrefix) throws IOException
{
Path resultsFile = Paths.get(RESULTS_FILE).toAbsolutePath();
ContactDetails[] results = book.search(keyPrefix);
FileWriter writer = new FileWriter(resultsFile.toString());
for(ContactDetails details : results) {
writer.write(details.toString());
writer.write('\n');
writer.write('\n');
}
writer.close();
}
/**
* Show the results from the most-recent call to
* saveSearchResults. As output is to the console, any
* problems are reported directly by this method.
*/
public void showSearchResults()
{
BufferedReader reader = null;
try {
Path resultsFile = Paths.get(RESULTS_FILE).toAbsolutePath();
reader = new BufferedReader(new FileReader(resultsFile.toString()));
System.out.println("Results ...");
String line;
line = reader.readLine();
while(line != null) {
System.out.println(line);
line = reader.readLine();
}
System.out.println();
}
catch(FileNotFoundException e) {
System.out.println("Unable to find the file: " +
RESULTS_FILE);
}
catch(IOException e) {
System.out.println("Error encountered reading the file: " +
RESULTS_FILE);
}
finally {
if(reader != null) {
// Catch any exception, but nothing can be done
// about it.
try {
reader.close();
}
catch(IOException e) {
System.out.println("Error on closing: " +
RESULTS_FILE);
}
}
}
}
/**
* Add further entries to the address book, from a text file.
* The file is assumed to contain one element per line,
* plus a blank line, for each entry:
* name \n phone \n address \n \n
* A line may be blank if that part of the details is missing.
* @param filename The text file containing the details.
* @throws IOException On input failure.
*/
public void addEntriesFromFile(String filename) throws IOException
{
// Make sure the file can be found.
URL resource = getClass().getResource(filename);
if(resource == null) {
throw new FileNotFoundException(filename);
}
filename = resource.getFile();
BufferedReader reader = new BufferedReader(
new FileReader(filename));
String name;
name = reader.readLine();
while(name != null) {
String phone = reader.readLine();
String address = reader.readLine();
// Discard the separating blank line.
reader.readLine();
book.addDetails(new ContactDetails(name, phone,
address));
name = reader.readLine();
}
reader.close();
}
/**
* Read the binary version of an address book from the given file.
* If the file name is not an absolute path, then it is assumed
* to be relative to the current project folder.
* @param sourceFile The file from where the details are to be read.
* @return The address book object.
* @throws IOException If the reading process fails for any reason.
*/
public AddressBook readFromFile(String sourceFile)
throws IOException, ClassNotFoundException
{
// Make sure the file can be found.
URL resource = getClass().getResource(sourceFile);
if(resource == null) {
throw new FileNotFoundException(sourceFile);
}
try {
File source = new File(resource.toURI());
ObjectInputStream is = new ObjectInputStream(
new FileInputStream(source));
AddressBook savedBook = (AddressBook) is.readObject();
is.close();
return savedBook;
}
catch(URISyntaxException e) {
throw new IOException("Unable to make a valid filename for " +
sourceFile);
}
}
/**
* Save a binary version of the address book to the given file.
* If the file name is not an absolute path, then it is assumed
* to be relative to the current project folder.
* @param destinationFile The file where the details are to be saved.
* @throws IOException If the saving process fails for any reason.
*/
public void saveToFile(String destinationFile) throws IOException
{
Path destination = Paths.get(destinationFile).toAbsolutePath();
ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream(destination.toString()));
os.writeObject(book);
os.close();
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=otherDetails
comment5.target=int\ compareTo(ContactDetails)
comment5.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment6.params=
comment6.target=java.lang.String\ toString()
comment6.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,125 @@
import java.io.Serializable;
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>, Serializable
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,28 @@
Project: address-book-io
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple address-book implementation.
How to start this project:
Either: Create an AddressBook object and add
contact details (such as name, address, and phone) to it.
Or: create an AddressBookDemo and use its getBook method.
Create an AddressBookFileHandler to illustrate some basic
input-output operations on an AddressBook.
User instructions:
+ Search results are saved to "results.txt" in the project folder.
+ The file "sample.txt", in the project, folder contains a sample
entry that can be added, using the addEntriesFromFile of
the AddressBookFileHandler.
+ A complete address book object may be saved, in binary form,
using the saveToFile method. This may be read in again with
readFromFile.

View File

@@ -0,0 +1,83 @@
#BlueJ package file
dependency1.from=AddressBookFileHandler
dependency1.to=AddressBook
dependency1.type=UsesDependency
dependency2.from=AddressBookFileHandler
dependency2.to=ContactDetails
dependency2.type=UsesDependency
dependency3.from=AddressBook
dependency3.to=ContactDetails
dependency3.type=UsesDependency
dependency4.from=AddressBookDemo
dependency4.to=AddressBook
dependency4.type=UsesDependency
dependency5.from=AddressBookDemo
dependency5.to=ContactDetails
dependency5.type=UsesDependency
objectbench.height=76
objectbench.width=842
package.editor.height=447
package.editor.width=734
package.editor.x=70
package.editor.y=80
package.numDependencies=5
package.numTargets=4
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=586
readme.editor.width=821
readme.editor.x=42
readme.editor.y=23
target1.editor.height=761
target1.editor.width=894
target1.editor.x=53
target1.editor.y=39
target1.height=60
target1.name=AddressBook
target1.naviview.expanded=true
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=140
target1.x=220
target1.y=160
target2.editor.height=715
target2.editor.width=874
target2.editor.x=50
target2.editor.y=60
target2.height=60
target2.name=ContactDetails
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=150
target2.x=340
target2.y=260
target3.editor.height=747
target3.editor.width=842
target3.editor.x=50
target3.editor.y=53
target3.height=60
target3.name=AddressBookDemo
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=150
target3.x=90
target3.y=60
target4.editor.height=719
target4.editor.width=819
target4.editor.x=43
target4.editor.y=23
target4.height=60
target4.name=AddressBookFileHandler
target4.naviview.expanded=true
target4.showInterface=false
target4.type=ClassTarget
target4.typeParameters=
target4.width=180
target4.x=470
target4.y=350

View File

@@ -0,0 +1,4 @@
chris
08459 700000
address 7

View File

@@ -0,0 +1,32 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ address\ book.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ a\ key\ that\ is\ currently\ in\ use.\n\ @param\ details\ The\ replacement\ details.\ Must\ not\ be\ null.\n\ @throws\ IllegalArgumentException\ If\ either\ argument\ is\ null.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\ This\ may\ be\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ zero\ length,\ but\ must\ not\ be\ null.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ Return\ the\ number\ of\ entries\ currently\ in\ the\n\ address\ book.\n\ @return\ The\ number\ of\ entries.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ the\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ The\ key\ should\ be\ one\ that\ is\ currently\ in\ use.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n\ @throws\ IllegalArgumentException\ If\ the\ key\ is\ null.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ Return\ all\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n\ @return\ A\ sorted\ list\ of\ the\ details.\n
comment9.params=
comment9.target=boolean\ consistentSize()
comment9.text=\n\ Check\ that\ the\ numberOfEntries\ field\ is\ consistent\ with\n\ the\ number\ of\ entries\ actually\ stored\ in\ the\ address\ book.\n\ @return\ true\ if\ the\ field\ is\ consistent,\ false\ otherwise.\n
numComments=10

View File

@@ -0,0 +1,187 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
* This version uses assert statements to check internal
* consistency.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
if(details == null) {
throw new IllegalArgumentException("Null details passed to addDetails.");
}
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
assert consistentSize() : "Inconsistent book size in addDetails";
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
This should be a key that is currently in use.
* @param details The replacement details. Must not be null.
* @throws IllegalArgumentException If either argument is null.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
if(details == null) {
throw new IllegalArgumentException("Null details passed to changeDetails.");
}
if(oldKey == null){
throw new IllegalArgumentException("Null key passed to changeDetails.");
}
if(keyInUse(oldKey)){
removeDetails(oldKey);
addDetails(details);
}
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on. This may be
* of zero length, but must not be null.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
if(keyPrefix != null) {
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
endOfSearch = true;
}
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove the entry with the given key from the address book.
* The key should be one that is currently in use.
* @param key One of the keys of the entry to be removed.
* @throws IllegalArgumentException If the key is null.
*/
public void removeDetails(String key)
{
if(key == null){
throw new IllegalArgumentException("Null key passed to removeDetails.");
}
if(keyInUse(key)) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
assert !keyInUse(key);
assert consistentSize() : "Inconsistent book size in removeDetails";
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
/**
* Check that the numberOfEntries field is consistent with
* the number of entries actually stored in the address book.
* @return true if the field is consistent, false otherwise.
*/
private boolean consistentSize()
{
Collection<ContactDetails> allEntries = book.values();
// Eliminate duplicates as we are using multiple keys.
Set<ContactDetails> uniqueEntries = new HashSet<>(allEntries);
int actualCount = uniqueEntries.size();
return numberOfEntries == actualCount;
}
}

View File

@@ -0,0 +1,35 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBookTest()
comment0.text=\n\ Set\ up\ the\ sample\ details.\n
comment1.params=
comment1.target=void\ setUp()
comment1.text=\n\ Sets\ up\ the\ test\ fixture.\n\n\ Called\ before\ every\ test\ case\ method.\n
comment10.params=
comment10.target=void\ testAddDetailsError()
comment10.text=\n\ Trigger\ an\ assertion\ error\ by\ adding\ revised\ details\ via\n\ addDetails\ rather\ than\ changeDetails.\n
comment2.params=
comment2.target=void\ tearDown()
comment2.text=\n\ Tears\ down\ the\ test\ fixture.\n\n\ Called\ after\ every\ test\ case\ method.\n
comment3.params=
comment3.target=void\ testGetNumberOfEntries()
comment3.text=\n\ Test\ that\ the\ count\ matches\ the\ number\ of\ sample\ entries.\n\ Then\ remove\ one\ and\ check\ for\ a\ reduced\ count.\n\ Then\ add\ one\ and\ check\ for\ an\ increased\ count.\n
comment4.params=
comment4.target=void\ testGetDetails()
comment4.text=\n\ Check\ that\ the\ existing\ contact\ can\ be\ found,\ and\ that\ a\ non-existent\n\ one\ cannot\ be\ found.\n
comment5.params=
comment5.target=void\ testKeyInUse()
comment5.text=\n\ Test\ that\ the\ name\ and\ phone\ numbers\ of\ an\ existing\ contact\n\ are\ registered\ as\ existing\ keys.\n
comment6.params=
comment6.target=void\ testAddDetails()
comment6.text=\n\ Test\ that\ a\ further\ contact\ can\ be\ added.\n
comment7.params=
comment7.target=void\ testRemoveDetails()
comment7.text=\n\ Test\ that\ we\ can\ add\ a\ further\ contact\ and\ then\ remove\ them.\n
comment8.params=
comment8.target=void\ testChangeDetails()
comment8.text=\n\ Test\ that\ details\ of\ an\ existing\ contact\ can\ be\ changed.\n
comment9.params=
comment9.target=void\ testSearch()
comment9.text=\n\ Test\ that\ a\ search\ for\ an\ existing\ contact\ finds\ one\ entry.\n\ Then\ test\ that\ a\ search\ for\ a\ non-existent\ contact\ fails\n\ to\ find\ any\ entries.\n\ Then\ add\ a\ new\ contact\ and\ test\ that\ a\ search\ finds\ them.\n
numComments=11

View File

@@ -0,0 +1,191 @@
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* An outline test class for AddressBook.
* Not all methods are tested. Neither are those methods
* that are tested tested thoroughly.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookTest
{
// Sample contact details.
private ContactDetails[] sampleDetails;
// An address book containing sample data.
private AddressBook book;
// A copy of one of the contact's details.
private ContactDetails existingContact;
// An additional contact.
private ContactDetails furtherContact;
// Revised details for an existing contact.
private ContactDetails revisedDetails;
/**
* Set up the sample details.
*/
public AddressBookTest()
{
// A sample of contact details to be stored in the book.
sampleDetails = new ContactDetails[] {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
}
/**
* Sets up the test fixture.
*
* Called before every test case method.
*/
@Before
public void setUp()
{
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
// Take a copy of the details of one of the contacts.
ContactDetails first = sampleDetails[0];
existingContact = new ContactDetails(first.getName(), first.getPhone(),
first.getAddress());
// Create a further contact who is not yet in the address book.
furtherContact = new ContactDetails("ian", "08459 900000", "address 9");
// Change the address of an existing contact.
revisedDetails = new ContactDetails(existingContact.getName(),
existingContact.getPhone(),
existingContact.getAddress() + "X");
}
/**
* Tears down the test fixture.
*
* Called after every test case method.
*/
@After
public void tearDown()
{
book = null;
existingContact = null;
furtherContact = null;
revisedDetails = null;
}
/**
* Test that the count matches the number of sample entries.
* Then remove one and check for a reduced count.
* Then add one and check for an increased count.
*/
@Test
public void testGetNumberOfEntries()
{
assertEquals(sampleDetails.length, book.getNumberOfEntries());
book.removeDetails(existingContact.getName());
assertEquals(sampleDetails.length - 1, book.getNumberOfEntries());
book.addDetails(furtherContact);
assertEquals(sampleDetails.length, book.getNumberOfEntries());
}
/**
* Check that the existing contact can be found, and that a non-existent
* one cannot be found.
*/
@Test
public void testGetDetails()
{
assertEquals(existingContact, book.getDetails(existingContact.getName()));
assertNull(book.getDetails(furtherContact.getName()));
}
/**
* Test that the name and phone numbers of an existing contact
* are registered as existing keys.
*/
@Test
public void testKeyInUse()
{
assertEquals(true, book.keyInUse(existingContact.getName()));
assertEquals(true, book.keyInUse(existingContact.getPhone()));
}
/**
* Test that a further contact can be added.
*/
@Test
public void testAddDetails()
{
assertEquals(false, book.keyInUse("ian"));
book.addDetails(furtherContact);
assertEquals(true, book.keyInUse("ian"));
assertEquals(sampleDetails.length + 1, book.getNumberOfEntries());
}
/**
* Test that we can add a further contact and then remove them.
*/
@Test
public void testRemoveDetails()
{
book.addDetails(furtherContact);
assertEquals(true, book.keyInUse("ian"));
book.removeDetails("ian");
assertEquals(false, book.keyInUse("ian"));
}
/**
* Test that details of an existing contact can be changed.
*/
@Test
public void testChangeDetails()
{
assertEquals(existingContact, book.getDetails(existingContact.getName()));
book.changeDetails(existingContact.getName(), revisedDetails);
assertEquals(revisedDetails, book.getDetails(revisedDetails.getName()));
}
/**
* Test that a search for an existing contact finds one entry.
* Then test that a search for a non-existent contact fails
* to find any entries.
* Then add a new contact and test that a search finds them.
*/
@Test
public void testSearch()
{
assertEquals(book.search(existingContact.getName()).length, 1);
assertEquals(book.search(furtherContact.getName()).length, 0);
book.addDetails(furtherContact);
assertEquals(book.search(furtherContact.getName()).length, 1);
}
/**
* Trigger an assertion error by adding revised details via
* addDetails rather than changeDetails.
*/
@Test
public void testAddDetailsError()
{
assertEquals(existingContact, book.getDetails(existingContact.getName()));
book.addDetails(revisedDetails);
assertEquals(existingContact, book.getDetails(existingContact.getName()));
assertEquals(revisedDetails, book.getDetails(revisedDetails.getName()));
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n\ @throws\ IllegalStateException\ If\ both\ name\ and\ phone\ are\ blank.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=
comment5.target=java.lang.String\ toString()
comment5.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment6.params=otherDetails
comment6.target=int\ compareTo(ContactDetails)
comment6.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,129 @@
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
* @throws IllegalStateException If both name and phone are blank.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
if(this.name.isEmpty() && this.phone.isEmpty()) {
throw new IllegalStateException(
"Either the name or phone must not be blank.");
}
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,14 @@
Project: address-book-junit
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple JUnit framework for testing an address-book implementation.
How to start this project:
Use AddressBookTest to test the methods of AddressBook.

View File

@@ -0,0 +1,65 @@
#BlueJ package file
dependency1.from=AddressBookTest
dependency1.to=ContactDetails
dependency1.type=UsesDependency
dependency2.from=AddressBook
dependency2.to=ContactDetails
dependency2.type=UsesDependency
dependency3.from=AddressBookTest
dependency3.to=AddressBook
dependency3.type=UsesDependency
objectbench.height=76
objectbench.width=842
package.editor.height=462
package.editor.width=734
package.editor.x=70
package.editor.y=80
package.numDependencies=3
package.numTargets=3
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=536
readme.editor.width=825
readme.editor.x=42
readme.editor.y=23
target1.editor.height=734
target1.editor.width=853
target1.editor.x=97
target1.editor.y=66
target1.height=60
target1.name=AddressBookTest
target1.naviview.expanded=true
target1.showInterface=false
target1.type=UnitTestTargetJunit4
target1.typeParameters=
target1.width=150
target1.x=150
target1.y=110
target2.association=AddressBookTest
target2.editor.height=742
target2.editor.width=934
target2.editor.x=50
target2.editor.y=58
target2.height=60
target2.name=AddressBook
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=150
target2.x=120
target2.y=140
target3.editor.height=725
target3.editor.width=939
target3.editor.x=50
target3.editor.y=60
target3.height=60
target3.name=ContactDetails
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=130
target3.x=350
target3.y=250

View File

@@ -0,0 +1,29 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ address\ book.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ @param\ details\ The\ replacement\ details.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ Return\ the\ number\ of\ entries\ currently\ in\ the\n\ address\ book.\n\ @return\ The\ number\ of\ entries.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ an\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ Return\ all\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n\ @return\ A\ sorted\ list\ of\ the\ details.\n
numComments=9

View File

@@ -0,0 +1,143 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
* @param details The replacement details.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
removeDetails(oldKey);
addDetails(details);
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
endOfSearch = true;
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove an entry with the given key from the address book.
* @param key One of the keys of the entry to be removed.
*/
public void removeDetails(String key)
{
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBookDemo()
comment0.text=\n\ Setup\ an\ AddressBook\ with\ sample\ data.\n\ The\ address\ book\ is\ passed\ to\ a\ GUI\ to\ provide\n\ a\ view\ of\ the\ data.\n
comment1.params=
comment1.target=void\ showInterface()
comment1.text=\n\ Allow\ the\ user\ to\ interact\ with\ the\ address\ book.\n
comment2.params=
comment2.target=AddressBook\ getBook()
comment2.text=\n\ @return\ The\ sample\ address\ book.\n
numComments=3

View File

@@ -0,0 +1,55 @@
/**
* Provide a simple demonstration of the AddressBook class.
* Sample data is added to the address book,
* and a GUI view is provided.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookDemo
{
private AddressBook book;
private AddressBookGUI interact;
/**
* Setup an AddressBook with sample data.
* The address book is passed to a GUI to provide
* a view of the data.
*/
public AddressBookDemo()
{
ContactDetails[] sampleDetails = {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
// Provide a GUI view of the address book.
interact = new AddressBookGUI(book);
}
/**
* Allow the user to interact with the address book.
*/
public void showInterface()
{
interact.setVisible(true);
}
/**
* @return The sample address book.
*/
public AddressBook getBook()
{
return book;
}
}

View File

@@ -0,0 +1,31 @@
#BlueJ class context
comment0.params=book
comment0.target=AddressBookGUI(AddressBook)
comment0.text=\n\ Create\ the\ frame\ with\ its\ panels.\n\ @param\ book\ The\ address\ book\ to\ be\ manipulated.\n
comment1.params=ev
comment1.target=void\ windowClosing(java.awt.event.WindowEvent)
comment10.params=
comment10.target=java.awt.Container\ setupListArea()
comment10.text=\n\ Set\ up\ the\ panel\ for\ listing\ the\ entries.\n\ @return\ The\ completed\ panel.\n
comment2.params=
comment2.target=void\ showWindow()
comment2.text=\n\ Show\ the\ window\ if\ it\ has\ been\ closed.\n
comment3.params=
comment3.target=java.awt.Dimension\ getPreferredSize()
comment3.text=\n\ @return\ The\ preferred\ size\ of\ this\ window.\n
comment4.params=
comment4.target=java.awt.Container\ setupDataEntry()
comment4.text=\n\ Set\ up\ the\ panel\ for\ data\ entry.\n\ @return\ The\ completed\ panel.\n
comment5.params=
comment5.target=java.awt.Container\ setupSearchArea()
comment5.text=\n\ Set\ up\ the\ panel\ for\ searching\ the\ entries.\n\ @return\ The\ completed\ panel.\n
comment6.params=ev
comment6.target=void\ changedUpdate(javax.swing.event.DocumentEvent)
comment7.params=ev
comment7.target=void\ insertUpdate(javax.swing.event.DocumentEvent)
comment8.params=ev
comment8.target=void\ removeUpdate(javax.swing.event.DocumentEvent)
comment9.params=
comment9.target=void\ research()
comment9.text=\n\ Search\ the\ address\ book\ and\ present\ the\ results\ unless\n\ the\ search\ string\ is\ empty,\ in\ which\ case\ the\ results\n\ area\ is\ cleared.\n
numComments=11

View File

@@ -0,0 +1,248 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
/**
* Provide a GUI view of an AddressBook.
* Different panes provide access to the data in the address book.
*
* One to search the address book.
*
* One to allow a set of contact details to be entered.
* The add button adds the data to the address book.
*
* One to show all the entries in the book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookGUI extends JFrame
{
// Size preferences for this frame.
private static final int PREFERRED_WIDTH = 500;
private static final int PREFERRED_HEIGHT = 500;
private static final Dimension PREFERRED_SIZE =
new Dimension(PREFERRED_WIDTH,PREFERRED_HEIGHT);
// The address book to be viewed and manipulated.
private AddressBook book;
/**
* Create the frame with its panels.
* @param book The address book to be manipulated.
*/
public AddressBookGUI(AddressBook book)
{
this.book = book;
setTitle("Address Book");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev)
{
setVisible(false);
}
});
final Container contentPane = getContentPane();
JTabbedPane tabbedArea = new JTabbedPane();
tabbedArea.add("Search the Entries", setupSearchArea());
tabbedArea.add("Enter New Details", setupDataEntry());
tabbedArea.add("List the Entries", setupListArea());
contentPane.add(tabbedArea);
setSize(PREFERRED_SIZE);
}
/**
* Show the window if it has been closed.
*/
public void showWindow()
{
setVisible(true);
}
/**
* @return The preferred size of this window.
*/
public Dimension getPreferredSize()
{
return PREFERRED_SIZE;
}
/**
* Set up the panel for data entry.
* @return The completed panel.
*/
private Container setupDataEntry()
{
// Set up the name field.
Box nameLabelArea = Box.createHorizontalBox();
nameLabelArea.add(new JLabel("Name", JLabel.LEFT));
nameLabelArea.add(Box.createGlue());
final JTextField nameField = new JTextField(50);
Box nameArea = Box.createVerticalBox();
nameArea.add(nameLabelArea);
nameArea.add(nameField);
// Set up the phone number area.
Box phoneLabelArea = Box.createHorizontalBox();
phoneLabelArea.add(new JLabel("Phone", JLabel.LEFT));
phoneLabelArea.add(Box.createGlue());
final JTextField phoneField = new JTextField(50);
Box phoneArea = Box.createVerticalBox();
phoneArea.add(phoneLabelArea);
phoneArea.add(phoneField);
// Set up the address area.
Box addressLabelArea = Box.createHorizontalBox();
addressLabelArea.add(new JLabel("Address", JLabel.LEFT));
addressLabelArea.add(Box.createGlue());
Box addressArea = Box.createVerticalBox();
final JTextArea address = new JTextArea(10, 50);
addressArea.add(addressLabelArea);
addressArea.add(address);
// Layout the entry-details fields in a panel.
Box singleLineFields = Box.createVerticalBox();
singleLineFields.add(nameArea);
singleLineFields.add(phoneArea);
JPanel detailsPanel = new JPanel();
detailsPanel.setLayout(new BorderLayout());
detailsPanel.add(singleLineFields, BorderLayout.NORTH);
detailsPanel.add(addressArea, BorderLayout.CENTER);
// Set up the buttons.
JPanel buttonArea = new JPanel();
JButton add = new JButton("Add");
JButton clear = new JButton("Clear");
// Take the necessary action to add the new details.
add.addActionListener(e -> {
book.addDetails(
new ContactDetails(nameField.getText(),
phoneField.getText(),
address.getText()));
}
);
// Clear the data-entry areas.
clear.addActionListener(e -> {
nameField.setText("");
phoneField.setText("");
address.setText("");
}
);
buttonArea.add(add);
buttonArea.add(clear);
// Layout the details area above the button area.
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(detailsPanel, BorderLayout.CENTER);
panel.add(buttonArea, BorderLayout.SOUTH);
return panel;
}
/**
* Set up the panel for searching the entries.
* @return The completed panel.
*/
private Container setupSearchArea()
{
// Set up the area for entering the search string.
Box searchLabelArea = Box.createHorizontalBox();
searchLabelArea.add(new JLabel("Search", JLabel.LEFT));
searchLabelArea.add(Box.createGlue());
final JTextField searchField = new JTextField(50);
Box searchArea = Box.createHorizontalBox();
searchArea.add(searchLabelArea);
searchArea.add(searchField);
// Set up the area where the resuts will be displayed.
final JTextArea resultList = new JTextArea(10,50);
resultList.setEditable(false);
JScrollPane scrollArea =
new JScrollPane(resultList,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Any change to the name field causes a new search of
// the address book to be made.
searchField.getDocument().addDocumentListener(new DocumentListener(){
public void changedUpdate(DocumentEvent ev)
{
research();
}
public void insertUpdate(DocumentEvent ev)
{
research();
}
public void removeUpdate(DocumentEvent ev)
{
research();
}
/**
* Search the address book and present the results unless
* the search string is empty, in which case the results
* area is cleared.
*/
private void research()
{
String searchString = searchField.getText();
StringBuilder buffer = new StringBuilder();
if(searchString.length() > 0) {
ContactDetails[] results = book.search(searchString);
for(int i = 0; i < results.length; i++) {
buffer.append(results[i]).append("\n\n");
}
}
resultList.setText(buffer.toString());
}
});
JPanel listArea = new JPanel();
listArea.setLayout(new BorderLayout());
listArea.add(searchArea, BorderLayout.NORTH);
listArea.add(scrollArea, BorderLayout.CENTER);
return listArea;
}
/**
* Set up the panel for listing the entries.
* @return The completed panel.
*/
private Container setupListArea()
{
// Set up the area where the details will be displayed.
final JTextArea details = new JTextArea(10, 50);
details.setEditable(false);
JScrollPane scrollArea =
new JScrollPane(details,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Set up the buttons.
JPanel buttonArea = new JPanel();
JButton list = new JButton("List");
JButton clear = new JButton("Clear");
// List all of the entries.
list.addActionListener(e -> details.setText(book.listDetails()));
// Clear the details area.
clear.addActionListener(e -> details.setText(""));
buttonArea.add(list);
buttonArea.add(clear);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(scrollArea, BorderLayout.CENTER);
panel.add(buttonArea, BorderLayout.SOUTH);
return panel;
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=otherDetails
comment5.target=int\ compareTo(ContactDetails)
comment5.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment6.params=
comment6.target=java.lang.String\ toString()
comment6.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,122 @@
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,18 @@
Project: address-book-v1g
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple address-book implementation.
How to start this project:
Either: Create an AddressBook object and add
contact details (such as name, address, and phone) to it.
Or: create an AddressBookDemo and call its showInterface method.
This will create a sample AddressBook along with a GUI for
the purposes of interactive experimentation.

View File

@@ -0,0 +1,86 @@
#BlueJ package file
dependency1.from=AddressBook
dependency1.to=ContactDetails
dependency1.type=UsesDependency
dependency2.from=AddressBookGUI
dependency2.to=AddressBook
dependency2.type=UsesDependency
dependency3.from=AddressBookDemo
dependency3.to=AddressBook
dependency3.type=UsesDependency
dependency4.from=AddressBookDemo
dependency4.to=AddressBookGUI
dependency4.type=UsesDependency
dependency5.from=AddressBookDemo
dependency5.to=ContactDetails
dependency5.type=UsesDependency
dependency6.from=AddressBookGUI
dependency6.to=ContactDetails
dependency6.type=UsesDependency
objectbench.height=76
objectbench.width=842
package.editor.height=444
package.editor.width=734
package.editor.x=70
package.editor.y=80
package.numDependencies=6
package.numTargets=4
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=597
readme.editor.width=794
readme.editor.x=43
readme.editor.y=23
target1.editor.height=777
target1.editor.width=1237
target1.editor.x=43
target1.editor.y=23
target1.height=60
target1.name=AddressBookGUI
target1.naviview.expanded=false
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=120
target1.x=240
target1.y=130
target2.editor.height=777
target2.editor.width=1181
target2.editor.x=43
target2.editor.y=23
target2.height=60
target2.name=AddressBook
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=110
target2.x=370
target2.y=220
target3.editor.height=700
target3.editor.width=900
target3.editor.x=86
target3.editor.y=86
target3.height=60
target3.name=ContactDetails
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=120
target3.x=510
target3.y=310
target4.editor.height=728
target4.editor.width=825
target4.editor.x=50
target4.editor.y=60
target4.height=60
target4.name=AddressBookDemo
target4.naviview.expanded=true
target4.showInterface=false
target4.type=ClassTarget
target4.typeParameters=
target4.width=130
target4.x=90
target4.y=40

View File

@@ -0,0 +1,29 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ address\ book.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ @param\ details\ The\ replacement\ details.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ Return\ the\ number\ of\ entries\ currently\ in\ the\n\ address\ book.\n\ @return\ The\ number\ of\ entries.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ an\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ Return\ all\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n\ @return\ A\ sorted\ list\ of\ the\ details.\n
numComments=9

View File

@@ -0,0 +1,144 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
* @param details The replacement details.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
removeDetails(oldKey);
addDetails(details);
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
// As the list is sorted, there won't be any more.
endOfSearch = true;
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove an entry with the given key from the address book.
* @param key One of the keys of the entry to be removed.
*/
public void removeDetails(String key)
{
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBookDemo()
comment0.text=\n\ Setup\ an\ AddressBook\ with\ sample\ data.\n\ The\ address\ book\ is\ passed\ to\ a\ GUI\ to\ provide\n\ a\ view\ of\ the\ data.\n
comment1.params=
comment1.target=void\ showInterface()
comment1.text=\n\ Allow\ the\ user\ to\ interact\ with\ the\ address\ book.\n
comment2.params=
comment2.target=AddressBook\ getBook()
comment2.text=\n\ @return\ The\ sample\ address\ book.\n
numComments=3

View File

@@ -0,0 +1,53 @@
/**
* Provide a simple demonstration of the AddressBook class.
* Sample data is added to the address book,
* and a text interface is provided.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookDemo
{
private AddressBook book;
private AddressBookTextInterface interaction;
/**
* Setup an AddressBook with sample data.
* The address book is passed to a GUI to provide
* a view of the data.
*/
public AddressBookDemo()
{
ContactDetails[] sampleDetails = {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
interaction = new AddressBookTextInterface(book);
}
/**
* Allow the user to interact with the address book.
*/
public void showInterface()
{
interaction.run();
}
/**
* @return The sample address book.
*/
public AddressBook getBook()
{
return book;
}
}

View File

@@ -0,0 +1,20 @@
#BlueJ class context
comment0.params=book
comment0.target=AddressBookTextInterface(AddressBook)
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ AddressBookTextInterface\n\ @param\ book\ The\ address\ book\ to\ be\ manipulated.\n
comment1.params=
comment1.target=void\ run()
comment1.text=\n\ Read\ a\ series\ of\ commands\ from\ the\ user\ to\ interact\n\ with\ the\ address\ book.\ Stop\ when\ the\ user\ types\ 'quit'.\n
comment2.params=
comment2.target=void\ add()
comment2.text=\n\ Add\ a\ new\ entry.\n
comment3.params=
comment3.target=void\ find()
comment3.text=\n\ Find\ entries\ matching\ a\ key\ prefix.\n
comment4.params=
comment4.target=void\ help()
comment4.text=\n\ List\ the\ available\ commands.\n
comment5.params=
comment5.target=void\ list()
comment5.text=\n\ List\ the\ address\ book's\ contents.\n
numComments=6

View File

@@ -0,0 +1,106 @@
/**
* Provide a textual interface to an AddressBook.
* Different commands provide access to the data in the address book.
*
* One to search the address book.
*
* One to allow a set of contact details to be entered.
*
* One to show all the entries in the book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookTextInterface
{
// The address book to be viewed and manipulated.
private AddressBook book;
// A parser for handling user commands.
private Parser parser;
/**
* Constructor for objects of class AddressBookTextInterface
* @param book The address book to be manipulated.
*/
public AddressBookTextInterface(AddressBook book)
{
this.book = book;
parser = new Parser();
}
/**
* Read a series of commands from the user to interact
* with the address book. Stop when the user types 'quit'.
*/
public void run()
{
System.out.println("Address Book.");
System.out.println("Type 'help' for a list of commands.");
String command;
do{
command = parser.getCommand();
if(command.equals("add")){
add();
}
else if(command.equals("list")){
list();
}
else if(command.equals("search")){
find();
}
else if(command.equals("help")){
help();
}
else{
// Do nothing.
}
} while(!(command.equals("quit")));
System.out.println("Goodbye.");
}
/**
* Add a new entry.
*/
private void add()
{
System.out.print("Name: ");
String name = parser.readLine();
System.out.print("Phone: ");
String phone = parser.readLine();
System.out.print("Address: ");
String address = parser.readLine();
book.addDetails(new ContactDetails(name, phone, address));
}
/**
* Find entries matching a key prefix.
*/
private void find()
{
System.out.println("Type a prefix of the key to be found.");
String prefix = parser.readLine();
ContactDetails[] results = book.search(prefix);
for(int i = 0; i < results.length; i++){
System.out.println(results[i]);
System.out.println("=====");
}
}
/**
* List the available commands.
*/
private void help()
{
parser.showCommands();
}
/**
* List the address book's contents.
*/
private void list()
{
System.out.println(book.listDetails());
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=CommandWords()
comment0.text=\n\ Constructor\ for\ CommandWords\n
comment1.params=aString
comment1.target=boolean\ isCommand(java.lang.String)
comment1.text=\n\ Check\ whether\ a\ given\ String\ is\ a\ valid\ command\ word.\n\ @param\ aString\ The\ string\ to\ be\ checked.\n\ @return\ true\ if\ it\ is\ valid,\ false\ if\ it\ isn't.\n
comment2.params=
comment2.target=void\ showAll()
comment2.text=\n\ Print\ all\ valid\ commands\ to\ System.out.\n
numComments=3

View File

@@ -0,0 +1,49 @@
/**
* This class holds an enumeration of all command words known
* to the program.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class CommandWords
{
// a constant array that holds all valid command words
private static final String validCommands[] = {
"add", "search", "list", "help", "quit",
};
/**
* Constructor for CommandWords
*/
public CommandWords()
{
}
/**
* Check whether a given String is a valid command word.
* @param aString The string to be checked.
* @return true if it is valid, false if it isn't.
*/
public boolean isCommand(String aString)
{
if(aString != null){
for(int i = 0; i < validCommands.length; i++) {
if(validCommands[i].equals(aString))
return true;
}
}
// if we get here, the string was not found in the commands
return false;
}
/**
* Print all valid commands to System.out.
*/
public void showAll()
{
for(String command : validCommands) {
System.out.print(command + " ");
}
System.out.println();
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=otherDetails
comment5.target=int\ compareTo(ContactDetails)
comment5.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment6.params=
comment6.target=java.lang.String\ toString()
comment6.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,122 @@
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,13 @@
#BlueJ class context
comment0.params=
comment0.target=Parser()
comment1.params=
comment1.target=java.lang.String\ getCommand()
comment1.text=\n\ Read\ the\ next\ command\ from\ the\ user.\n\ The\ returned\ command\ will\ be\ valid.\n\ @return\ A\ valid\ command.\n
comment2.params=
comment2.target=void\ showCommands()
comment2.text=\n\ Print\ out\ a\ list\ of\ valid\ command\ words.\n
comment3.params=
comment3.target=java.lang.String\ readLine()
comment3.text=\n\ @return\ A\ line\ of\ text\ from\ the\ user.\n
numComments=4

View File

@@ -0,0 +1,65 @@
import java.util.Scanner;
/**
* A class that reads input lines from the user.
* Input is filtered via getCommand for valid commands.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class Parser
{
// Hold all valid command words.
private CommandWords commands;
private Scanner reader;
public Parser()
{
commands = new CommandWords();
reader = new Scanner(System.in);
}
/**
* Read the next command from the user.
* The returned command will be valid.
* @return A valid command.
*/
public String getCommand()
{
String command = null;
do {
// Print a prompt.
System.out.print("> ");
String word = reader.next();
// Discard the rest of the line.
readLine();
if(commands.isCommand(word)) {
command = word;
}
else{
System.out.println("Unrecognized command: " + word);
System.out.print("Valid commands are: ");
commands.showAll();
}
} while(command == null);
return command;
}
/**
* Print out a list of valid command words.
*/
public void showCommands()
{
commands.showAll();
}
/**
* @return A line of text from the user.
*/
public String readLine()
{
return reader.nextLine();
}
}

View File

@@ -0,0 +1,18 @@
Project: address-book-v1t
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple address-book implementation.
How to start this project:
Either: Create an AddressBook object and add
contact details (such as name, address, and phone) to it.
Or: create an AddressBookDemo and call its showInterface method.
This will create a sample AddressBook along with a text
interface for the purposes of interactive experimentation.

View File

@@ -0,0 +1,118 @@
#BlueJ package file
dependency1.from=Parser
dependency1.to=CommandWords
dependency1.type=UsesDependency
dependency2.from=AddressBook
dependency2.to=ContactDetails
dependency2.type=UsesDependency
dependency3.from=AddressBookDemo
dependency3.to=AddressBook
dependency3.type=UsesDependency
dependency4.from=AddressBookDemo
dependency4.to=AddressBookTextInterface
dependency4.type=UsesDependency
dependency5.from=AddressBookDemo
dependency5.to=ContactDetails
dependency5.type=UsesDependency
dependency6.from=AddressBookTextInterface
dependency6.to=AddressBook
dependency6.type=UsesDependency
dependency7.from=AddressBookTextInterface
dependency7.to=Parser
dependency7.type=UsesDependency
dependency8.from=AddressBookTextInterface
dependency8.to=ContactDetails
dependency8.type=UsesDependency
objectbench.height=76
objectbench.width=893
package.editor.height=503
package.editor.width=785
package.editor.x=70
package.editor.y=80
package.numDependencies=8
package.numTargets=6
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=583
readme.editor.width=831
readme.editor.x=43
readme.editor.y=23
target1.editor.height=727
target1.editor.width=755
target1.editor.x=43
target1.editor.y=23
target1.height=60
target1.name=AddressBook
target1.naviview.expanded=true
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=110
target1.x=190
target1.y=230
target2.editor.height=707
target2.editor.width=860
target2.editor.x=50
target2.editor.y=60
target2.height=60
target2.name=ContactDetails
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=120
target2.x=290
target2.y=370
target3.editor.height=729
target3.editor.width=846
target3.editor.x=50
target3.editor.y=60
target3.height=60
target3.name=AddressBookDemo
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=140
target3.x=70
target3.y=70
target4.editor.height=708
target4.editor.width=803
target4.editor.x=50
target4.editor.y=60
target4.height=60
target4.name=AddressBookTextInterface
target4.naviview.expanded=true
target4.showInterface=false
target4.type=ClassTarget
target4.typeParameters=
target4.width=180
target4.x=360
target4.y=130
target5.editor.height=725
target5.editor.width=853
target5.editor.x=50
target5.editor.y=60
target5.height=60
target5.name=CommandWords
target5.naviview.expanded=true
target5.showInterface=false
target5.type=ClassTarget
target5.typeParameters=
target5.width=130
target5.x=610
target5.y=330
target6.editor.height=724
target6.editor.width=826
target6.editor.x=50
target6.editor.y=60
target6.height=60
target6.name=Parser
target6.naviview.expanded=true
target6.showInterface=false
target6.type=ClassTarget
target6.typeParameters=
target6.width=90
target6.x=520
target6.y=230

View File

@@ -0,0 +1,29 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ address\ book.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ a\ key\ that\ is\ currently\ in\ use.\n\ @param\ details\ The\ replacement\ details.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\ This\ may\ be\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ zero\ length,\ but\ must\ not\ be\ null.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ Return\ the\ number\ of\ entries\ currently\ in\ the\n\ address\ book.\n\ @return\ The\ number\ of\ entries.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ the\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ The\ key\ should\ be\ one\ that\ is\ currently\ in\ use.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ Return\ all\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n\ @return\ A\ sorted\ list\ of\ the\ details.\n
numComments=9

View File

@@ -0,0 +1,154 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
if(details != null) {
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
}
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
This should be a key that is currently in use.
* @param details The replacement details.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
if(keyInUse(oldKey) && details != null) {
removeDetails(oldKey);
addDetails(details);
}
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on. This may be
* of zero length, but must not be null.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
if(keyPrefix != null) {
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
endOfSearch = true;
}
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove the entry with the given key from the address book.
* The key should be one that is currently in use.
* @param key One of the keys of the entry to be removed.
*/
public void removeDetails(String key)
{
if(keyInUse(key)) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
}

View File

@@ -0,0 +1,15 @@
#BlueJ class context
comment0.target=class\ AddressBookDemo
comment0.text=\nProvide\ a\ simple\ demonstration\ of\ the\ AddressBook\ class.\nSample\ data\ is\ added\ to\ the\ address\ book,\nand\ a\ GUI\ view\ is\ provided.\n\n@author\ David\ J.\ Barnes\ and\ Michael\ Kolling.\n@version\ 2005.07.08\n\n
comment1.target=book
comment2.params=
comment2.target=AddressBookDemo()
comment2.text=\nSetup\ an\ AddressBook\ with\ sample\ data.\nThe\ address\ book\ is\ passed\ to\ a\ GUI\ to\ provide\na\ view\ of\ the\ data.\n\n
comment3.target=interact
comment4.params=
comment4.target=AddressBook\ getBook()
comment4.text=\n@return\ The\ sample\ address\ book.\n\n
comment5.params=
comment5.target=void\ showInterface()
comment5.text=\nAllow\ the\ user\ to\ interact\ with\ the\ address\ book.\n\n
numComments=6

View File

@@ -0,0 +1,55 @@
/**
* Provide a simple demonstration of the AddressBook class.
* Sample data is added to the address book,
* and a GUI view is provided.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookDemo
{
private AddressBook book;
private AddressBookGUI interact;
/**
* Setup an AddressBook with sample data.
* The address book is passed to a GUI to provide
* a view of the data.
*/
public AddressBookDemo()
{
ContactDetails[] sampleDetails = {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
// Provide a GUI view of the address book.
interact = new AddressBookGUI(book);
}
/**
* Allow the user to interact with the address book.
*/
public void showInterface()
{
interact.setVisible(true);
}
/**
* @return The sample address book.
*/
public AddressBook getBook()
{
return book;
}
}

View File

@@ -0,0 +1,31 @@
#BlueJ class context
comment0.params=book
comment0.target=AddressBookGUI(AddressBook)
comment0.text=\n\ Create\ the\ frame\ with\ its\ panels.\n\ @param\ book\ The\ address\ book\ to\ be\ manipulated.\n
comment1.params=ev
comment1.target=void\ windowClosing(java.awt.event.WindowEvent)
comment10.params=
comment10.target=java.awt.Container\ setupListArea()
comment10.text=\n\ Set\ up\ the\ panel\ for\ listing\ the\ entries.\n\ @return\ The\ completed\ panel.\n
comment2.params=
comment2.target=void\ showWindow()
comment2.text=\n\ Show\ the\ window\ if\ it\ has\ been\ closed.\n
comment3.params=
comment3.target=java.awt.Dimension\ getPreferredSize()
comment3.text=\n\ @return\ The\ preferred\ size\ of\ this\ window.\n
comment4.params=
comment4.target=java.awt.Container\ setupDataEntry()
comment4.text=\n\ Set\ up\ the\ panel\ for\ data\ entry.\n\ @return\ The\ completed\ panel.\n
comment5.params=
comment5.target=java.awt.Container\ setupSearchArea()
comment5.text=\n\ Set\ up\ the\ panel\ for\ searching\ the\ entries.\n\ @return\ The\ completed\ panel.\n
comment6.params=ev
comment6.target=void\ changedUpdate(javax.swing.event.DocumentEvent)
comment7.params=ev
comment7.target=void\ insertUpdate(javax.swing.event.DocumentEvent)
comment8.params=ev
comment8.target=void\ removeUpdate(javax.swing.event.DocumentEvent)
comment9.params=
comment9.target=void\ research()
comment9.text=\n\ Search\ the\ address\ book\ and\ present\ the\ results\ unless\n\ the\ search\ string\ is\ empty,\ in\ which\ case\ the\ results\n\ area\ is\ cleared.\n
numComments=11

View File

@@ -0,0 +1,248 @@
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
/**
* Provide a GUI view of an AddressBook.
* Different panes provide access to the data in the address book.
*
* One to search the address book.
*
* One to allow a set of contact details to be entered.
* The add button adds the data to the address book.
*
* One to show all the entries in the book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookGUI extends JFrame
{
// Size preferences for this frame.
private static final int PREFERRED_WIDTH = 500;
private static final int PREFERRED_HEIGHT = 500;
private static final Dimension PREFERRED_SIZE =
new Dimension(PREFERRED_WIDTH,PREFERRED_HEIGHT);
// The address book to be viewed and manipulated.
private AddressBook book;
/**
* Create the frame with its panels.
* @param book The address book to be manipulated.
*/
public AddressBookGUI(AddressBook book)
{
this.book = book;
setTitle("Address Book");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev)
{
setVisible(false);
}
});
final Container contentPane = getContentPane();
JTabbedPane tabbedArea = new JTabbedPane();
tabbedArea.add("Search the Entries", setupSearchArea());
tabbedArea.add("Enter New Details", setupDataEntry());
tabbedArea.add("List the Entries", setupListArea());
contentPane.add(tabbedArea);
setSize(PREFERRED_SIZE);
}
/**
* Show the window if it has been closed.
*/
public void showWindow()
{
setVisible(true);
}
/**
* @return The preferred size of this window.
*/
public Dimension getPreferredSize()
{
return PREFERRED_SIZE;
}
/**
* Set up the panel for data entry.
* @return The completed panel.
*/
private Container setupDataEntry()
{
// Set up the name field.
Box nameLabelArea = Box.createHorizontalBox();
nameLabelArea.add(new JLabel("Name", JLabel.LEFT));
nameLabelArea.add(Box.createGlue());
final JTextField nameField = new JTextField(50);
Box nameArea = Box.createVerticalBox();
nameArea.add(nameLabelArea);
nameArea.add(nameField);
// Set up the phone number area.
Box phoneLabelArea = Box.createHorizontalBox();
phoneLabelArea.add(new JLabel("Phone", JLabel.LEFT));
phoneLabelArea.add(Box.createGlue());
final JTextField phoneField = new JTextField(50);
Box phoneArea = Box.createVerticalBox();
phoneArea.add(phoneLabelArea);
phoneArea.add(phoneField);
// Set up the address area.
Box addressLabelArea = Box.createHorizontalBox();
addressLabelArea.add(new JLabel("Address", JLabel.LEFT));
addressLabelArea.add(Box.createGlue());
Box addressArea = Box.createVerticalBox();
final JTextArea address = new JTextArea(10, 50);
addressArea.add(addressLabelArea);
addressArea.add(address);
// Layout the entry-details fields in a panel.
Box singleLineFields = Box.createVerticalBox();
singleLineFields.add(nameArea);
singleLineFields.add(phoneArea);
JPanel detailsPanel = new JPanel();
detailsPanel.setLayout(new BorderLayout());
detailsPanel.add(singleLineFields, BorderLayout.NORTH);
detailsPanel.add(addressArea, BorderLayout.CENTER);
// Set up the buttons.
JPanel buttonArea = new JPanel();
JButton add = new JButton("Add");
JButton clear = new JButton("Clear");
// Take the necessary action to add the new details.
add.addActionListener(e -> {
book.addDetails(
new ContactDetails(nameField.getText(),
phoneField.getText(),
address.getText()));
}
);
// Clear the data-entry areas.
clear.addActionListener(e -> {
nameField.setText("");
phoneField.setText("");
address.setText("");
}
);
buttonArea.add(add);
buttonArea.add(clear);
// Layout the details area above the button area.
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(detailsPanel, BorderLayout.CENTER);
panel.add(buttonArea, BorderLayout.SOUTH);
return panel;
}
/**
* Set up the panel for searching the entries.
* @return The completed panel.
*/
private Container setupSearchArea()
{
// Set up the area for entering the search string.
Box searchLabelArea = Box.createHorizontalBox();
searchLabelArea.add(new JLabel("Search", JLabel.LEFT));
searchLabelArea.add(Box.createGlue());
final JTextField searchField = new JTextField(50);
Box searchArea = Box.createHorizontalBox();
searchArea.add(searchLabelArea);
searchArea.add(searchField);
// Set up the area where the resuts will be displayed.
final JTextArea resultList = new JTextArea(10,50);
resultList.setEditable(false);
JScrollPane scrollArea =
new JScrollPane(resultList,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Any change to the name field causes a new search of
// the address book to be made.
searchField.getDocument().addDocumentListener(new DocumentListener(){
public void changedUpdate(DocumentEvent ev)
{
research();
}
public void insertUpdate(DocumentEvent ev)
{
research();
}
public void removeUpdate(DocumentEvent ev)
{
research();
}
/**
* Search the address book and present the results unless
* the search string is empty, in which case the results
* area is cleared.
*/
private void research()
{
String searchString = searchField.getText();
StringBuilder buffer = new StringBuilder();
if(searchString.length() > 0) {
ContactDetails[] results = book.search(searchString);
for(int i = 0; i < results.length; i++) {
buffer.append(results[i].toString()).append("\n\n");
}
}
resultList.setText(buffer.toString());
}
});
JPanel listArea = new JPanel();
listArea.setLayout(new BorderLayout());
listArea.add(searchArea, BorderLayout.NORTH);
listArea.add(scrollArea, BorderLayout.CENTER);
return listArea;
}
/**
* Set up the panel for listing the entries.
* @return The completed panel.
*/
private Container setupListArea()
{
// Set up the area where the details will be displayed.
final JTextArea details = new JTextArea(10, 50);
details.setEditable(false);
JScrollPane scrollArea =
new JScrollPane(details,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Set up the buttons.
JPanel buttonArea = new JPanel();
JButton list = new JButton("List");
JButton clear = new JButton("Clear");
// List all of the entries.
list.addActionListener(e -> details.setText(book.listDetails()));
// Clear the details area.
clear.addActionListener(e -> details.setText(""));
buttonArea.add(list);
buttonArea.add(clear);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(scrollArea, BorderLayout.CENTER);
panel.add(buttonArea, BorderLayout.SOUTH);
return panel;
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=otherDetails
comment5.target=int\ compareTo(ContactDetails)
comment5.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment6.params=
comment6.target=java.lang.String\ toString()
comment6.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,122 @@
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,18 @@
Project: address-book-v2g
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple address-book implementation.
How to start this project:
Either: Create an AddressBook object and add
contact details (such as name, address, and phone) to it.
Or: create an AddressBookDemo and call its run method.
This will create a sample AddressBook along with a GUI for
the purposes of interactive experimentation.

View File

@@ -0,0 +1,86 @@
#BlueJ package file
dependency1.from=AddressBook
dependency1.to=ContactDetails
dependency1.type=UsesDependency
dependency2.from=AddressBookGUI
dependency2.to=AddressBook
dependency2.type=UsesDependency
dependency3.from=AddressBookDemo
dependency3.to=AddressBook
dependency3.type=UsesDependency
dependency4.from=AddressBookDemo
dependency4.to=AddressBookGUI
dependency4.type=UsesDependency
dependency5.from=AddressBookDemo
dependency5.to=ContactDetails
dependency5.type=UsesDependency
dependency6.from=AddressBookGUI
dependency6.to=ContactDetails
dependency6.type=UsesDependency
objectbench.height=76
objectbench.width=869
package.editor.height=494
package.editor.width=761
package.editor.x=70
package.editor.y=80
package.numDependencies=6
package.numTargets=4
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=598
readme.editor.width=768
readme.editor.x=37
readme.editor.y=23
target1.editor.height=740
target1.editor.width=851
target1.editor.x=37
target1.editor.y=23
target1.height=60
target1.name=AddressBookGUI
target1.naviview.expanded=true
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=140
target1.x=230
target1.y=170
target2.editor.height=692
target2.editor.width=826
target2.editor.x=53
target2.editor.y=23
target2.height=60
target2.name=AddressBook
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=120
target2.x=390
target2.y=250
target3.editor.height=700
target3.editor.width=900
target3.editor.x=53
target3.editor.y=23
target3.height=60
target3.name=ContactDetails
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=130
target3.x=530
target3.y=330
target4.editor.height=730
target4.editor.width=871
target4.editor.x=50
target4.editor.y=60
target4.height=60
target4.name=AddressBookDemo
target4.naviview.expanded=true
target4.showInterface=false
target4.type=ClassTarget
target4.typeParameters=
target4.width=140
target4.x=70
target4.y=90

View File

@@ -0,0 +1,29 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ notebook.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ a\ key\ that\ is\ currently\ in\ use.\n\ @param\ details\ The\ replacement\ details.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\ This\ may\ be\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ zero\ length,\ but\ must\ not\ be\ null.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ @return\ The\ number\ of\ entries\ currently\ in\ the\n\ \ \ \ \ \ \ \ \ address\ book.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ the\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ The\ key\ should\ be\ one\ that\ is\ currently\ in\ use.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ @return\ All\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n
numComments=9

View File

@@ -0,0 +1,154 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
if(details != null) {
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
}
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
This should be a key that is currently in use.
* @param details The replacement details.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
if(keyInUse(oldKey) && details != null) {
removeDetails(oldKey);
addDetails(details);
}
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on. This may be
* of zero length, but must not be null.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
if(keyPrefix != null) {
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
endOfSearch = true;
}
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove the entry with the given key from the address book.
* The key should be one that is currently in use.
* @param key One of the keys of the entry to be removed.
*/
public void removeDetails(String key)
{
if(keyInUse(key)) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBookDemo()
comment0.text=\n\ Setup\ an\ AddressBook\ with\ sample\ data.\n\ The\ address\ book\ is\ passed\ to\ a\ GUI\ to\ provide\n\ a\ view\ of\ the\ data.\n
comment1.params=
comment1.target=void\ showInterface()
comment1.text=\n\ Allow\ the\ user\ to\ interact\ with\ the\ address\ book.\n
comment2.params=
comment2.target=AddressBook\ getBook()
comment2.text=\n\ @return\ The\ sample\ address\ book.\n
numComments=3

View File

@@ -0,0 +1,53 @@
/**
* Provide a simple demonstration of the AddressBook class.
* Sample data is added to the address book,
* and a text interface is provided.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookDemo
{
private AddressBook book;
private AddressBookTextInterface interaction;
/**
* Setup an AddressBook with sample data.
* The address book is passed to a GUI to provide
* a view of the data.
*/
public AddressBookDemo()
{
ContactDetails[] sampleDetails = {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
interaction = new AddressBookTextInterface(book);
}
/**
* Allow the user to interact with the address book.
*/
public void showInterface()
{
interaction.run();
}
/**
* @return The sample address book.
*/
public AddressBook getBook()
{
return book;
}
}

View File

@@ -0,0 +1,23 @@
#BlueJ class context
comment0.params=book
comment0.target=AddressBookTextInterface(AddressBook)
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ AddressBookTextInterface\n\ @param\ book\ The\ address\ book\ to\ be\ manipulated.\n
comment1.params=
comment1.target=void\ run()
comment1.text=\n\ Read\ a\ series\ of\ commands\ from\ the\ user\ to\ interact\n\ with\ the\ address\ book.\ Stop\ when\ the\ user\ types\ 'quit'.\n
comment2.params=
comment2.target=void\ add()
comment2.text=\n\ Add\ a\ new\ entry.\n
comment3.params=
comment3.target=void\ get()
comment3.text=\n\ Find\ an\ entry\ matching\ a\ key.\n
comment4.params=
comment4.target=void\ find()
comment4.text=\n\ Find\ entries\ matching\ a\ key\ prefix.\n
comment5.params=
comment5.target=void\ help()
comment5.text=\n\ List\ the\ available\ commands.\n
comment6.params=
comment6.target=void\ list()
comment6.text=\n\ List\ the\ address\ book's\ contents.\n
numComments=7

View File

@@ -0,0 +1,120 @@
/**
* Provide a textual interface to an AddressBook.
* Different commands provide access to the data in the address book.
*
* One to search the address book.
*
* One to allow a set of contact details to be entered.
*
* One to show all the entries in the book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookTextInterface
{
// The address book to be viewed and manipulated.
private AddressBook book;
// A parser for handling user commands.
private Parser parser;
/**
* Constructor for objects of class AddressBookTextInterface
* @param book The address book to be manipulated.
*/
public AddressBookTextInterface(AddressBook book)
{
this.book = book;
parser = new Parser();
}
/**
* Read a series of commands from the user to interact
* with the address book. Stop when the user types 'quit'.
*/
public void run()
{
System.out.println("Address Book.");
System.out.println("Type 'help' for a list of commands.");
String command;
do{
command = parser.getCommand();
if(command.equals("add")){
add();
}
else if(command.equals("get")){
get();
}
else if(command.equals("list")){
list();
}
else if(command.equals("search")){
find();
}
else if(command.equals("help")){
help();
}
else{
// Do nothing.
}
} while(!(command.equals("quit")));
System.out.println("Goodbye.");
}
/**
* Add a new entry.
*/
private void add()
{
System.out.print("Name: ");
String name = parser.readLine();
System.out.print("Phone: ");
String phone = parser.readLine();
System.out.print("Address: ");
String address = parser.readLine();
book.addDetails(new ContactDetails(name, phone, address));
}
/**
* Find an entry matching a key.
*/
private void get()
{
System.out.println("Type the key of the entry.");
String key = parser.readLine();
ContactDetails result = book.getDetails(key);
System.out.println(result);
}
/**
* Find entries matching a key prefix.
*/
private void find()
{
System.out.println("Type a prefix of the key to be found.");
String prefix = parser.readLine();
ContactDetails[] results = book.search(prefix);
for(int i = 0; i < results.length; i++){
System.out.println(results[i]);
System.out.println("=====");
}
}
/**
* List the available commands.
*/
private void help()
{
parser.showCommands();
}
/**
* List the address book's contents.
*/
private void list()
{
System.out.println(book.listDetails());
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=CommandWords()
comment0.text=\n\ Constructor\ for\ CommandWords\n
comment1.params=aString
comment1.target=boolean\ isCommand(java.lang.String)
comment1.text=\n\ Check\ whether\ a\ given\ String\ is\ a\ valid\ command\ word.\n\ @param\ aString\ The\ string\ to\ be\ checked.\n\ @return\ true\ if\ it\ is\ valid,\ false\ if\ it\ isn't.\n
comment2.params=
comment2.target=void\ showAll()
comment2.text=\n\ Print\ all\ valid\ commands\ to\ System.out.\n
numComments=3

View File

@@ -0,0 +1,49 @@
/**
* This class holds an enumeration of all command words known
* to the program.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class CommandWords
{
// a constant array that holds all valid command words
private static final String validCommands[] = {
"add", "get", "search", "list", "help", "quit",
};
/**
* Constructor for CommandWords
*/
public CommandWords()
{
}
/**
* Check whether a given String is a valid command word.
* @param aString The string to be checked.
* @return true if it is valid, false if it isn't.
*/
public boolean isCommand(String aString)
{
if(aString != null){
for(int i = 0; i < validCommands.length; i++) {
if(validCommands[i].equals(aString))
return true;
}
}
// if we get here, the string was not found in the commands
return false;
}
/**
* Print all valid commands to System.out.
*/
public void showAll()
{
for(String command : validCommands) {
System.out.print(command + " ");
}
System.out.println();
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=otherDetails
comment5.target=int\ compareTo(ContactDetails)
comment5.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment6.params=
comment6.target=java.lang.String\ toString()
comment6.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,122 @@
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,13 @@
#BlueJ class context
comment0.params=
comment0.target=Parser()
comment1.params=
comment1.target=java.lang.String\ getCommand()
comment1.text=\n\ Read\ the\ next\ command\ from\ the\ user.\n\ The\ returned\ command\ will\ be\ valid.\n\ @return\ A\ valid\ command.\n
comment2.params=
comment2.target=void\ showCommands()
comment2.text=\n\ Print\ out\ a\ list\ of\ valid\ command\ words.\n
comment3.params=
comment3.target=java.lang.String\ readLine()
comment3.text=\n\ @return\ A\ line\ of\ text\ from\ the\ user.\n
numComments=4

View File

@@ -0,0 +1,65 @@
import java.util.Scanner;
/**
* A class that reads input lines from the user.
* Input is filtered via getCommand for valid commands.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class Parser
{
// Hold all valid command words.
private CommandWords commands;
private Scanner reader;
public Parser()
{
commands = new CommandWords();
reader = new Scanner(System.in);
}
/**
* Read the next command from the user.
* The returned command will be valid.
* @return A valid command.
*/
public String getCommand()
{
String command = null;
do {
// Print a prompt.
System.out.print("> ");
String word = reader.next();
// Discard the rest of the line.
readLine();
if(commands.isCommand(word)) {
command = word;
}
else{
System.out.println("Unrecognized command: " + word);
System.out.print("Valid commands are: ");
commands.showAll();
}
} while(command == null);
return command;
}
/**
* Print out a list of valid command words.
*/
public void showCommands()
{
commands.showAll();
}
/**
* @return A line of text from the user.
*/
public String readLine()
{
return reader.nextLine();
}
}

View File

@@ -0,0 +1,18 @@
Project: address-book-v2t
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple address-book implementation.
How to start this project:
Either: Create an AddressBook object and add
contact details (such as name, address, and phone) to it.
Or: create an AddressBookDemo and call its showInterface method.
This will create a sample AddressBook along with a text
interface for the purposes of interactive experimentation.

View File

@@ -0,0 +1,118 @@
#BlueJ package file
dependency1.from=Parser
dependency1.to=CommandWords
dependency1.type=UsesDependency
dependency2.from=AddressBook
dependency2.to=ContactDetails
dependency2.type=UsesDependency
dependency3.from=AddressBookDemo
dependency3.to=AddressBook
dependency3.type=UsesDependency
dependency4.from=AddressBookDemo
dependency4.to=AddressBookTextInterface
dependency4.type=UsesDependency
dependency5.from=AddressBookDemo
dependency5.to=ContactDetails
dependency5.type=UsesDependency
dependency6.from=AddressBookTextInterface
dependency6.to=AddressBook
dependency6.type=UsesDependency
dependency7.from=AddressBookTextInterface
dependency7.to=Parser
dependency7.type=UsesDependency
dependency8.from=AddressBookTextInterface
dependency8.to=ContactDetails
dependency8.type=UsesDependency
objectbench.height=76
objectbench.width=863
package.editor.height=473
package.editor.width=755
package.editor.x=70
package.editor.y=80
package.numDependencies=8
package.numTargets=6
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=547
readme.editor.width=779
readme.editor.x=37
readme.editor.y=23
target1.editor.height=777
target1.editor.width=1243
target1.editor.x=53
target1.editor.y=23
target1.height=60
target1.name=AddressBook
target1.naviview.expanded=true
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=120
target1.x=160
target1.y=240
target2.editor.height=735
target2.editor.width=849
target2.editor.x=50
target2.editor.y=60
target2.height=60
target2.name=ContactDetails
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=130
target2.x=250
target2.y=360
target3.editor.height=722
target3.editor.width=867
target3.editor.x=50
target3.editor.y=60
target3.height=60
target3.name=AddressBookDemo
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=130
target3.x=60
target3.y=70
target4.editor.height=726
target4.editor.width=861
target4.editor.x=50
target4.editor.y=60
target4.height=60
target4.name=AddressBookTextInterface
target4.naviview.expanded=true
target4.showInterface=false
target4.type=ClassTarget
target4.typeParameters=
target4.width=180
target4.x=310
target4.y=140
target5.editor.height=738
target5.editor.width=854
target5.editor.x=50
target5.editor.y=60
target5.height=60
target5.name=CommandWords
target5.naviview.expanded=true
target5.showInterface=false
target5.type=ClassTarget
target5.typeParameters=
target5.width=140
target5.x=580
target5.y=360
target6.editor.height=740
target6.editor.width=852
target6.editor.x=50
target6.editor.y=60
target6.height=60
target6.name=Parser
target6.naviview.expanded=true
target6.showInterface=false
target6.type=ClassTarget
target6.typeParameters=
target6.width=100
target6.x=500
target6.y=240

View File

@@ -0,0 +1,29 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBook()
comment0.text=\n\ Perform\ any\ initialization\ for\ the\ address\ book.\n
comment1.params=key
comment1.target=ContactDetails\ getDetails(java.lang.String)
comment1.text=\n\ Look\ up\ a\ name\ or\ phone\ number\ and\ return\ the\n\ corresponding\ contact\ details.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ The\ details\ corresponding\ to\ the\ key.\n
comment2.params=key
comment2.target=boolean\ keyInUse(java.lang.String)
comment2.text=\n\ Return\ whether\ or\ not\ the\ current\ key\ is\ in\ use.\n\ @param\ key\ The\ name\ or\ number\ to\ be\ looked\ up.\n\ @return\ true\ if\ the\ key\ is\ in\ use,\ false\ otherwise.\n
comment3.params=details
comment3.target=void\ addDetails(ContactDetails)
comment3.text=\n\ Add\ a\ new\ set\ of\ details\ to\ the\ address\ book.\n\ @param\ details\ The\ details\ to\ associate\ with\ the\ person.\n
comment4.params=oldKey\ details
comment4.target=void\ changeDetails(java.lang.String,\ ContactDetails)
comment4.text=\n\ Change\ the\ details\ previously\ stored\ under\ the\ given\ key.\n\ @param\ oldKey\ One\ of\ the\ keys\ used\ to\ store\ the\ details.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ a\ key\ that\ is\ currently\ in\ use.\n\ @param\ details\ The\ replacement\ details.\ Must\ not\ be\ null.\n\ @throws\ IllegalArgumentException\ If\ either\ argument\ is\ null.\n
comment5.params=keyPrefix
comment5.target=ContactDetails[]\ search(java.lang.String)
comment5.text=\n\ Search\ for\ all\ details\ stored\ under\ a\ key\ that\ starts\ with\n\ the\ given\ prefix.\n\ @param\ keyPrefix\ The\ key\ prefix\ to\ search\ on.\ This\ may\ be\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ zero\ length,\ but\ must\ not\ be\ null.\n\ @return\ An\ array\ of\ those\ details\ that\ have\ been\ found.\n
comment6.params=
comment6.target=int\ getNumberOfEntries()
comment6.text=\n\ Return\ the\ number\ of\ entries\ currently\ in\ the\n\ address\ book.\n\ @return\ The\ number\ of\ entries.\n
comment7.params=key
comment7.target=void\ removeDetails(java.lang.String)
comment7.text=\n\ Remove\ the\ entry\ with\ the\ given\ key\ from\ the\ address\ book.\n\ The\ key\ should\ be\ one\ that\ is\ currently\ in\ use.\n\ @param\ key\ One\ of\ the\ keys\ of\ the\ entry\ to\ be\ removed.\n\ @throws\ IllegalArgumentException\ If\ the\ key\ is\ null.\n
comment8.params=
comment8.target=java.lang.String\ listDetails()
comment8.text=\n\ Return\ all\ the\ contact\ details,\ sorted\ according\n\ to\ the\ sort\ order\ of\ the\ ContactDetails\ class.\n\ @return\ A\ sorted\ list\ of\ the\ details.\n
numComments=9

View File

@@ -0,0 +1,166 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* A class to maintain an arbitrary number of contact details.
* Details are indexed by both name and phone number.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBook
{
// Storage for an arbitrary number of details.
private TreeMap<String, ContactDetails> book;
private int numberOfEntries;
/**
* Perform any initialization for the address book.
*/
public AddressBook()
{
book = new TreeMap<>();
numberOfEntries = 0;
}
/**
* Look up a name or phone number and return the
* corresponding contact details.
* @param key The name or number to be looked up.
* @return The details corresponding to the key.
*/
public ContactDetails getDetails(String key)
{
return book.get(key);
}
/**
* Return whether or not the current key is in use.
* @param key The name or number to be looked up.
* @return true if the key is in use, false otherwise.
*/
public boolean keyInUse(String key)
{
return book.containsKey(key);
}
/**
* Add a new set of details to the address book.
* @param details The details to associate with the person.
*/
public void addDetails(ContactDetails details)
{
if(details == null) {
throw new IllegalArgumentException("Null details passed to addDetails.");
}
book.put(details.getName(), details);
book.put(details.getPhone(), details);
numberOfEntries++;
}
/**
* Change the details previously stored under the given key.
* @param oldKey One of the keys used to store the details.
This should be a key that is currently in use.
* @param details The replacement details. Must not be null.
* @throws IllegalArgumentException If either argument is null.
*/
public void changeDetails(String oldKey,
ContactDetails details)
{
if(details == null) {
throw new IllegalArgumentException("Null details passed to changeDetails.");
}
if(oldKey == null){
throw new IllegalArgumentException("Null key passed to changeDetails.");
}
if(keyInUse(oldKey)){
removeDetails(oldKey);
addDetails(details);
}
}
/**
* Search for all details stored under a key that starts with
* the given prefix.
* @param keyPrefix The key prefix to search on. This may be
* of zero length, but must not be null.
* @return An array of those details that have been found.
*/
public ContactDetails[] search(String keyPrefix)
{
// Build a list of the matches.
List<ContactDetails> matches = new LinkedList<>();
if(keyPrefix != null) {
// Find keys that are equal-to or greater-than the prefix.
SortedMap<String, ContactDetails> tail = book.tailMap(keyPrefix);
Iterator<String> it = tail.keySet().iterator();
// Stop when we find a mismatch.
boolean endOfSearch = false;
while(!endOfSearch && it.hasNext()) {
String key = it.next();
if(key.startsWith(keyPrefix)) {
matches.add(book.get(key));
}
else {
endOfSearch = true;
}
}
}
ContactDetails[] results = new ContactDetails[matches.size()];
matches.toArray(results);
return results;
}
/**
* Return the number of entries currently in the
* address book.
* @return The number of entries.
*/
public int getNumberOfEntries()
{
return numberOfEntries;
}
/**
* Remove the entry with the given key from the address book.
* The key should be one that is currently in use.
* @param key One of the keys of the entry to be removed.
* @throws IllegalArgumentException If the key is null.
*/
public void removeDetails(String key)
{
if(key == null){
throw new IllegalArgumentException("Null key passed to removeDetails.");
}
if(keyInUse(key)) {
ContactDetails details = book.get(key);
book.remove(details.getName());
book.remove(details.getPhone());
numberOfEntries--;
}
}
/**
* Return all the contact details, sorted according
* to the sort order of the ContactDetails class.
* @return A sorted list of the details.
*/
public String listDetails()
{
// Because each entry is stored under two keys, it is
// necessary to build a set of the ContactDetails. This
// eliminates duplicates.
StringBuilder allEntries = new StringBuilder();
Set<ContactDetails> sortedDetails = new TreeSet<>(book.values());
for(ContactDetails details : sortedDetails) {
allEntries.append(details).append("\n\n");
}
return allEntries.toString();
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=AddressBookDemo()
comment0.text=\n\ Setup\ an\ AddressBook\ with\ sample\ data.\n\ The\ address\ book\ is\ passed\ to\ a\ GUI\ to\ provide\n\ a\ view\ of\ the\ data.\n
comment1.params=
comment1.target=void\ showInterface()
comment1.text=\n\ Allow\ the\ user\ to\ interact\ with\ the\ address\ book.\n
comment2.params=
comment2.target=AddressBook\ getBook()
comment2.text=\n\ @return\ The\ sample\ address\ book.\n
numComments=3

View File

@@ -0,0 +1,53 @@
/**
* Provide a simple demonstration of the AddressBook class.
* Sample data is added to the address book,
* and a text interface is provided.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookDemo
{
private AddressBook book;
private AddressBookTextInterface interaction;
/**
* Setup an AddressBook with sample data.
* The address book is passed to a GUI to provide
* a view of the data.
*/
public AddressBookDemo()
{
ContactDetails[] sampleDetails = {
new ContactDetails("david", "08459 100000", "address 1"),
new ContactDetails("michael", "08459 200000", "address 2"),
new ContactDetails("john", "08459 300000", "address 3"),
new ContactDetails("helen", "08459 400000", "address 4"),
new ContactDetails("emma", "08459 500000", "address 5"),
new ContactDetails("kate", "08459 600000", "address 6"),
new ContactDetails("chris", "08459 700000", "address 7"),
new ContactDetails("ruth", "08459 800000", "address 8"),
};
book = new AddressBook();
for(ContactDetails details : sampleDetails) {
book.addDetails(details);
}
interaction = new AddressBookTextInterface(book);
}
/**
* Allow the user to interact with the address book.
*/
public void showInterface()
{
interaction.run();
}
/**
* @return The sample address book.
*/
public AddressBook getBook()
{
return book;
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=book
comment0.target=AddressBookTextInterface(AddressBook)
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ AddressBookTextInterface\n\ @param\ book\ The\ address\ book\ to\ be\ manipulated.\n
comment1.params=
comment1.target=void\ run()
comment1.text=\n\ Read\ a\ series\ of\ commands\ from\ the\ user\ to\ interact\n\ with\ the\ address\ book.\ Stop\ when\ the\ user\ types\ 'quit'.\n
comment2.params=
comment2.target=void\ add()
comment2.text=\n\ Add\ a\ new\ entry.\n
comment3.params=
comment3.target=void\ get()
comment3.text=\n\ Find\ an\ entry\ matching\ a\ key.\n
comment4.params=
comment4.target=void\ remove()
comment4.text=\n\ Remove\ an\ entry\ matching\ a\ key.\n
comment5.params=
comment5.target=void\ find()
comment5.text=\n\ Find\ entries\ matching\ a\ key\ prefix.\n
comment6.params=
comment6.target=void\ help()
comment6.text=\n\ List\ the\ available\ commands.\n
comment7.params=
comment7.target=void\ list()
comment7.text=\n\ List\ the\ address\ book's\ contents.\n
numComments=8

View File

@@ -0,0 +1,133 @@
/**
* Provide a textual interface to an AddressBook.
* Different commands provide access to the data in the address book.
*
* One to search the address book.
*
* One to allow a set of contact details to be entered.
*
* One to show all the entries in the book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class AddressBookTextInterface
{
// The address book to be viewed and manipulated.
private AddressBook book;
// A parser for handling user commands.
private Parser parser;
/**
* Constructor for objects of class AddressBookTextInterface
* @param book The address book to be manipulated.
*/
public AddressBookTextInterface(AddressBook book)
{
this.book = book;
parser = new Parser();
}
/**
* Read a series of commands from the user to interact
* with the address book. Stop when the user types 'quit'.
*/
public void run()
{
System.out.println("Address Book.");
System.out.println("Type 'help' for a list of commands.");
String command;
do{
command = parser.getCommand();
if(command.equals("add")){
add();
}
else if(command.equals("get")){
get();
}
else if(command.equals("list")){
list();
}
else if(command.equals("search")){
find();
}
else if(command.equals("remove")){
remove();
}
else if(command.equals("help")){
help();
}
else{
// Do nothing.
}
} while(!(command.equals("quit")));
System.out.println("Goodbye.");
}
/**
* Add a new entry.
*/
private void add()
{
System.out.print("Name: ");
String name = parser.readLine();
System.out.print("Phone: ");
String phone = parser.readLine();
System.out.print("Address: ");
String address = parser.readLine();
book.addDetails(new ContactDetails(name, phone, address));
}
/**
* Find an entry matching a key.
*/
private void get()
{
System.out.println("Type the key of the entry.");
String key = parser.readLine();
ContactDetails result = book.getDetails(key);
System.out.println(result);
}
/**
* Remove an entry matching a key.
*/
private void remove()
{
System.out.println("Type the key of the entry.");
String key = parser.readLine();
book.removeDetails(key);
}
/**
* Find entries matching a key prefix.
*/
private void find()
{
System.out.println("Type a prefix of the key to be found.");
String prefix = parser.readLine();
ContactDetails[] results = book.search(prefix);
for(int i = 0; i < results.length; i++){
System.out.println(results[i]);
System.out.println("=====");
}
}
/**
* List the available commands.
*/
private void help()
{
parser.showCommands();
}
/**
* List the address book's contents.
*/
private void list()
{
System.out.println(book.listDetails());
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=CommandWords()
comment0.text=\n\ Constructor\ for\ CommandWords\n
comment1.params=aString
comment1.target=boolean\ isCommand(java.lang.String)
comment1.text=\n\ Check\ whether\ a\ given\ String\ is\ a\ valid\ command\ word.\n\ @param\ aString\ The\ string\ to\ be\ checked.\n\ @return\ true\ if\ it\ is\ valid,\ false\ if\ it\ isn't.\n
comment2.params=
comment2.target=void\ showAll()
comment2.text=\n\ Print\ all\ valid\ commands\ to\ System.out.\n
numComments=3

View File

@@ -0,0 +1,49 @@
/**
* This class holds an enumeration of all command words known
* to the program.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class CommandWords
{
// a constant array that holds all valid command words
private static final String validCommands[] = {
"add", "get", "search", "list", "remove", "help", "quit",
};
/**
* Constructor for CommandWords
*/
public CommandWords()
{
}
/**
* Check whether a given String is a valid command word.
* @param aString The string to be checked.
* @return true if it is valid, false if it isn't.
*/
public boolean isCommand(String aString)
{
if(aString != null){
for(int i = 0; i < validCommands.length; i++) {
if(validCommands[i].equals(aString))
return true;
}
}
// if we get here, the string was not found in the commands
return false;
}
/**
* Print all valid commands to System.out.
*/
public void showAll()
{
for(String command : validCommands) {
System.out.print(command + " ");
}
System.out.println();
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=name\ phone\ address
comment0.target=ContactDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
comment0.text=\n\ Set\ up\ the\ contact\ details.\ All\ details\ are\ trimmed\ to\ remove\n\ trailing\ white\ space.\n\ @param\ name\ The\ name.\n\ @param\ phone\ The\ phone\ number.\n\ @param\ address\ The\ address.\n\ @throws\ IllegalStateException\ If\ both\ name\ and\ phone\ are\ blank.\n
comment1.params=
comment1.target=java.lang.String\ getName()
comment1.text=\n\ @return\ The\ name.\n
comment2.params=
comment2.target=java.lang.String\ getPhone()
comment2.text=\n\ @return\ The\ telephone\ number.\n
comment3.params=
comment3.target=java.lang.String\ getAddress()
comment3.text=\n\ @return\ The\ address.\n
comment4.params=other
comment4.target=boolean\ equals(java.lang.Object)
comment4.text=\n\ Test\ for\ content\ equality\ between\ two\ objects.\n\ @param\ other\ The\ object\ to\ compare\ to\ this\ one.\n\ @return\ true\ if\ the\ argument\ object\ is\ a\ set\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ of\ contact\ details\ with\ matching\ attributes.\n
comment5.params=otherDetails
comment5.target=int\ compareTo(ContactDetails)
comment5.text=\n\ Compare\ these\ details\ against\ another\ set,\ for\ the\ purpose\n\ of\ sorting.\ The\ fields\ are\ sorted\ by\ name,\ phone,\ and\ address.\n\ @param\ otherDetails\ The\ details\ to\ be\ compared\ against.\n\ @return\ a\ negative\ integer\ if\ this\ comes\ before\ the\ parameter,\n\ \ \ \ \ \ \ \ \ zero\ if\ they\ are\ equal\ and\ a\ positive\ integer\ if\ this\n\ \ \ \ \ \ \ \ \ comes\ after\ the\ second.\n
comment6.params=
comment6.target=java.lang.String\ toString()
comment6.text=\n\ @return\ A\ multi-line\ string\ containing\ the\ name,\ phone,\ and\ address.\n
comment7.params=
comment7.target=int\ hashCode()
comment7.text=\n\ Compute\ a\ hashcode\ using\ the\ rules\ to\ be\ found\ in\n\ "Effective\ Java",\ by\ Joshua\ Bloch.\n\ @return\ A\ hashcode\ for\ ContactDetails.\n
numComments=8

View File

@@ -0,0 +1,129 @@
/**
* Name, address and telephone number details.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class ContactDetails implements Comparable<ContactDetails>
{
private String name;
private String phone;
private String address;
/**
* Set up the contact details. All details are trimmed to remove
* trailing white space.
* @param name The name.
* @param phone The phone number.
* @param address The address.
* @throws IllegalStateException If both name and phone are blank.
*/
public ContactDetails(String name, String phone, String address)
{
// Use blank strings if any of the arguments is null.
if(name == null) {
name = "";
}
if(phone == null) {
phone = "";
}
if(address == null) {
address = "";
}
this.name = name.trim();
this.phone = phone.trim();
this.address = address.trim();
if(this.name.isEmpty() && this.phone.isEmpty()) {
throw new IllegalStateException(
"Either the name or phone must not be blank.");
}
}
/**
* @return The name.
*/
public String getName()
{
return name;
}
/**
* @return The telephone number.
*/
public String getPhone()
{
return phone;
}
/**
* @return The address.
*/
public String getAddress()
{
return address;
}
/**
* Test for content equality between two objects.
* @param other The object to compare to this one.
* @return true if the argument object is a set
* of contact details with matching attributes.
*/
public boolean equals(Object other)
{
if(other instanceof ContactDetails) {
ContactDetails otherDetails = (ContactDetails) other;
return name.equals(otherDetails.getName()) &&
phone.equals(otherDetails.getPhone()) &&
address.equals(otherDetails.getAddress());
}
else {
return false;
}
}
/**
* Compare these details against another set, for the purpose
* of sorting. The fields are sorted by name, phone, and address.
* @param otherDetails The details to be compared against.
* @return a negative integer if this comes before the parameter,
* zero if they are equal and a positive integer if this
* comes after the second.
*/
public int compareTo(ContactDetails otherDetails)
{
int comparison = name.compareTo(otherDetails.getName());
if(comparison != 0){
return comparison;
}
comparison = phone.compareTo(otherDetails.getPhone());
if(comparison != 0){
return comparison;
}
return address.compareTo(otherDetails.getAddress());
}
/**
* @return A multi-line string containing the name, phone, and address.
*/
public String toString()
{
return name + "\n" + phone + "\n" + address;
}
/**
* Compute a hashcode using the rules to be found in
* "Effective Java", by Joshua Bloch.
* @return A hashcode for ContactDetails.
*/
public int hashCode()
{
int code = 17;
code = 37 * code + name.hashCode();
code = 37 * code + phone.hashCode();
code = 37 * code + address.hashCode();
return code;
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=key
comment0.target=NoMatchingDetailsException(java.lang.String)
comment0.text=\n\ Store\ the\ details\ in\ error.\n\ @param\ key\ The\ key\ with\ no\ match.\n
comment1.params=
comment1.target=java.lang.String\ getKey()
comment1.text=\n\ @return\ The\ key\ in\ error.\n
comment2.params=
comment2.target=java.lang.String\ toString()
comment2.text=\n\ @return\ A\ diagnostic\ string\ containing\ the\ key\ in\ error.\n
numComments=3

View File

@@ -0,0 +1,37 @@
/**
* Capture a key that failed to match an entry
* in the address book.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class NoMatchingDetailsException extends Exception
{
// The key with no match.
private String key;
/**
* Store the details in error.
* @param key The key with no match.
*/
public NoMatchingDetailsException(String key)
{
this.key = key;
}
/**
* @return The key in error.
*/
public String getKey()
{
return key;
}
/**
* @return A diagnostic string containing the key in error.
*/
public String toString()
{
return "No details matching: " + key + " were found.";
}
}

View File

@@ -0,0 +1,13 @@
#BlueJ class context
comment0.params=
comment0.target=Parser()
comment1.params=
comment1.target=java.lang.String\ getCommand()
comment1.text=\n\ Read\ the\ next\ command\ from\ the\ user.\n\ The\ returned\ command\ will\ be\ valid.\n\ @return\ A\ valid\ command.\n
comment2.params=
comment2.target=void\ showCommands()
comment2.text=\n\ Print\ out\ a\ list\ of\ valid\ command\ words.\n
comment3.params=
comment3.target=java.lang.String\ readLine()
comment3.text=\n\ @return\ A\ line\ of\ text\ from\ the\ user.\n
numComments=4

View File

@@ -0,0 +1,65 @@
import java.util.Scanner;
/**
* A class that reads input lines from the user.
* Input is filtered via getCommand for valid commands.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class Parser
{
// Hold all valid command words.
private CommandWords commands;
private Scanner reader;
public Parser()
{
commands = new CommandWords();
reader = new Scanner(System.in);
}
/**
* Read the next command from the user.
* The returned command will be valid.
* @return A valid command.
*/
public String getCommand()
{
String command = null;
do {
// Print a prompt.
System.out.print("> ");
String word = reader.next();
// Discard the rest of the line.
readLine();
if(commands.isCommand(word)) {
command = word;
}
else{
System.out.println("Unrecognized command: " + word);
System.out.print("Valid commands are: ");
commands.showAll();
}
} while(command == null);
return command;
}
/**
* Print out a list of valid command words.
*/
public void showCommands()
{
commands.showAll();
}
/**
* @return A line of text from the user.
*/
public String readLine()
{
return reader.nextLine();
}
}

View File

@@ -0,0 +1,19 @@
Project: address-book-v3t
Authors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 12 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
A simple address-book implementation.
This version includes some argument-validity checking.
How to start this project:
Either: Create an AddressBook object and add
contact details (such as name, address, and phone) to it.
Or: create an AddressBookDemo and call its showInterface method.
This will create a sample AddressBook along with a text
interface for the purposes of interactive experimentation.

View File

@@ -0,0 +1,131 @@
#BlueJ package file
dependency1.from=Parser
dependency1.to=CommandWords
dependency1.type=UsesDependency
dependency2.from=AddressBook
dependency2.to=ContactDetails
dependency2.type=UsesDependency
dependency3.from=AddressBookDemo
dependency3.to=AddressBook
dependency3.type=UsesDependency
dependency4.from=AddressBookDemo
dependency4.to=AddressBookTextInterface
dependency4.type=UsesDependency
dependency5.from=AddressBookDemo
dependency5.to=ContactDetails
dependency5.type=UsesDependency
dependency6.from=AddressBookTextInterface
dependency6.to=AddressBook
dependency6.type=UsesDependency
dependency7.from=AddressBookTextInterface
dependency7.to=Parser
dependency7.type=UsesDependency
dependency8.from=AddressBookTextInterface
dependency8.to=ContactDetails
dependency8.type=UsesDependency
objectbench.height=76
objectbench.width=949
package.editor.height=500
package.editor.width=841
package.editor.x=70
package.editor.y=80
package.numDependencies=8
package.numTargets=7
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=588
readme.editor.width=813
readme.editor.x=36
readme.editor.y=23
target1.editor.height=735
target1.editor.width=862
target1.editor.x=53
target1.editor.y=23
target1.height=60
target1.name=AddressBook
target1.naviview.expanded=true
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=120
target1.x=180
target1.y=220
target2.editor.height=748
target2.editor.width=826
target2.editor.x=50
target2.editor.y=52
target2.height=60
target2.name=ContactDetails
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=130
target2.x=280
target2.y=340
target3.editor.height=744
target3.editor.width=820
target3.editor.x=50
target3.editor.y=56
target3.height=60
target3.name=AddressBookDemo
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=130
target3.x=90
target3.y=30
target4.editor.height=738
target4.editor.width=823
target4.editor.x=153
target4.editor.y=42
target4.height=60
target4.name=NoMatchingDetailsException
target4.naviview.expanded=true
target4.showInterface=false
target4.type=ClassTarget
target4.typeParameters=
target4.width=190
target4.x=590
target4.y=110
target5.editor.height=700
target5.editor.width=900
target5.editor.x=37
target5.editor.y=23
target5.height=60
target5.name=AddressBookTextInterface
target5.naviview.expanded=true
target5.showInterface=false
target5.type=ClassTarget
target5.typeParameters=
target5.width=180
target5.x=360
target5.y=110
target6.editor.height=730
target6.editor.width=843
target6.editor.x=50
target6.editor.y=60
target6.height=60
target6.name=CommandWords
target6.naviview.expanded=true
target6.showInterface=false
target6.type=ClassTarget
target6.typeParameters=
target6.width=140
target6.x=600
target6.y=340
target7.editor.height=712
target7.editor.width=845
target7.editor.x=50
target7.editor.y=60
target7.height=60
target7.name=Parser
target7.naviview.expanded=true
target7.showInterface=false
target7.type=ClassTarget
target7.typeParameters=
target7.width=110
target7.x=510
target7.y=220

View File

@@ -0,0 +1,6 @@
#BlueJ class context
comment0.target=InputReader()
comment0.text=\nCreate\ a\ new\ InputReader\ that\ reads\ text\ from\ the\ text\ terminal.\n\n
comment1.target=HashSet<String>\ getInput()
comment1.text=\nRead\ a\ line\ of\ text\ from\ standard\ input\ (the\ text\ terminal),\nand\ return\ it\ as\ a\ set\ of\ words.\n\n@return\ \ A\ set\ of\ Strings,\ where\ each\ String\ is\ one\ of\ the\ \nwords\ typed\ by\ the\ user\n\n
numComments=2

View File

@@ -0,0 +1,45 @@
import java.util.HashSet;
import java.util.Scanner;
/**
* InputReader reads typed text input from the standard text terminal.
* The text typed by a user is then chopped into words, and a set of words
* is provided.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class InputReader
{
private Scanner reader;
/**
* Create a new InputReader that reads text from the text terminal.
*/
public InputReader()
{
reader = new Scanner(System.in);
}
/**
* Read a line of text from standard input (the text terminal),
* and return it as a set of words.
*
* @return A set of Strings, where each String is one of the
* words typed by the user
*/
public HashSet<String> getInput()
{
System.out.print("> "); // print prompt
String inputLine = reader.nextLine().trim().toLowerCase();
String[] wordArray = inputLine.split(" "); // split at spaces
// add words from array into hashset
HashSet<String> words = new HashSet<>();
for(String word : wordArray) {
words.add(word);
}
return words;
}
}

View File

@@ -0,0 +1,29 @@
TechSupport - the DodgySoft Technical support system.
This project is part of the material for the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
This project is discussed in chapters 5 and 12.
This project implements a technical support system for customers of the
DodgySoft software company. Users can describe their software problems and
get advice instantly!
The idea is based on Eliza - a famous program described by Joseph Weizenbaum
in 1966. (Do a web search for "Eliza" and "Weizenbaum" if you want to know
more about this.)
In fact, it is much more primitive than Eliza. But that's enough to match the
quality of many software companies' technical support advice... ;-)
To start this program, create a SupportSystem object and execute the "start"
method.
Then start describing your problem by typing in the terminal window.
The purpose of this project is to demonstrate and study library classes, such
as ArrayList, HashMap, HashSet, and Random.

View File

@@ -0,0 +1,17 @@
#BlueJ class context
comment0.params=
comment0.target=Responder()
comment0.text=\r\n\ Construct\ a\ Responder\r\n
comment1.params=words
comment1.target=java.lang.String\ generateResponse(java.util.HashSet)
comment1.text=\r\n\ Generate\ a\ response\ from\ a\ given\ set\ of\ input\ words.\r\n\ \r\n\ @param\ words\ \ A\ set\ of\ words\ entered\ by\ the\ user\r\n\ @return\ \ \ \ \ \ \ A\ string\ that\ should\ be\ displayed\ as\ the\ response\r\n
comment2.params=
comment2.target=void\ fillResponseMap()
comment2.text=\r\n\ Enter\ all\ the\ known\ keywords\ and\ their\ associated\ responses\r\n\ into\ our\ response\ map.\r\n
comment3.params=
comment3.target=void\ fillDefaultResponses()
comment3.text=\r\n\ Build\ up\ a\ list\ of\ default\ responses\ from\ which\ we\ can\ pick\r\n\ if\ we\ don't\ know\ what\ else\ to\ say.\r\n
comment4.params=
comment4.target=java.lang.String\ pickDefaultResponse()
comment4.text=\r\n\ Randomly\ select\ and\ return\ one\ of\ the\ default\ responses.\r\n\ @return\ \ \ \ \ A\ random\ default\ response\r\n
numComments=5

View File

@@ -0,0 +1,157 @@
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.*;
/**
* The responder class represents a response generator object.
* It is used to generate an automatic response, based on specified input.
* Input is presented to the responder as a set of words, and based on those
* words the responder will generate a String that represents the response.
*
* Internally, the reponder uses a HashMap to associate words with response
* strings and a list of default responses. If any of the input words is found
* in the HashMap, the corresponding response is returned. If none of the input
* words is recognized, one of the default responses is randomly chosen.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class Responder
{
// Used to map key words to responses.
private HashMap<String, String> responseMap;
// Default responses to use if we don't recognise a word.
private ArrayList<String> defaultResponses;
// The name of the file containing the default responses.
private static final String FILE_OF_DEFAULT_RESPONSES = "default.txt";
private Random randomGenerator;
/**
* Construct a Responder
*/
public Responder()
{
responseMap = new HashMap<>();
defaultResponses = new ArrayList<>();
fillResponseMap();
fillDefaultResponses();
randomGenerator = new Random();
}
/**
* Generate a response from a given set of input words.
*
* @param words A set of words entered by the user
* @return A string that should be displayed as the response
*/
public String generateResponse(HashSet<String> words)
{
Iterator<String> it = words.iterator();
while(it.hasNext()) {
String word = it.next();
String response = responseMap.get(word);
if(response != null) {
return response;
}
}
// If we get here, none of the words from the input line was recognized.
// In this case we pick one of our default responses (what we say when
// we cannot think of anything else to say...)
return pickDefaultResponse();
}
/**
* Enter all the known keywords and their associated responses
* into our response map.
*/
private void fillResponseMap()
{
responseMap.put("crash",
"Well, it never crashes on our system. It must have something\n" +
"to do with your system. Tell me more about your configuration.");
responseMap.put("crashes",
"Well, it never crashes on our system. It must have something\n" +
"to do with your system. Tell me more about your configuration.");
responseMap.put("slow",
"I think this has to do with your hardware. Upgrading your processor\n" +
"should solve all performance problems. Have you got a problem with\n" +
"our software?");
responseMap.put("performance",
"Performance was quite adequate in all our tests. Are you running\n" +
"any other processes in the background?");
responseMap.put("bug",
"Well, you know, all software has some bugs. But our software engineers\n" +
"are working very hard to fix them. Can you describe the problem a bit\n" +
"further?");
responseMap.put("buggy",
"Well, you know, all software has some bugs. But our software engineers\n" +
"are working very hard to fix them. Can you describe the problem a bit\n" +
"further?");
responseMap.put("windows",
"This is a known bug to do with the Windows operating system. Please\n" +
"report it to Microsoft. There is nothing we can do about this.");
responseMap.put("macintosh",
"This is a known bug to do with the Mac operating system. Please\n" +
"report it to Apple. There is nothing we can do about this.");
responseMap.put("expensive",
"The cost of our product is quite competitive. Have you looked around\n" +
"and really compared our features?");
responseMap.put("installation",
"The installation is really quite straight forward. We have tons of\n" +
"wizards that do all the work for you. Have you read the installation\n" +
"instructions?");
responseMap.put("memory",
"If you read the system requirements carefully, you will see that the\n" +
"specified memory requirements are 1.5 giga byte. You really should\n" +
"upgrade your memory. Anything else you want to know?");
responseMap.put("linux",
"We take Linux support very seriously. But there are some problems.\n" +
"Most have to do with incompatible glibc versions. Can you be a bit\n" +
"more precise?");
responseMap.put("bluej",
"Ahhh, BlueJ, yes. We tried to buy out those guys long ago, but\n" +
"they simply won't sell... Stubborn people they are. Nothing we can\n" +
"do about it, I'm afraid.");
}
/**
* Build up a list of default responses from which we can pick
* if we don't know what else to say.
*/
private void fillDefaultResponses()
{
Charset charset = Charset.forName("US-ASCII");
Path path = Paths.get(FILE_OF_DEFAULT_RESPONSES);
try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
String response = reader.readLine();
while(response != null) {
defaultResponses.add(response);
response = reader.readLine();
}
}
catch(FileNotFoundException e) {
System.err.println("Unable to open " + FILE_OF_DEFAULT_RESPONSES);
}
catch(IOException e) {
System.err.println("A problem was encountered reading " +
FILE_OF_DEFAULT_RESPONSES);
}
// Make sure we have at least one response.
if(defaultResponses.size() == 0) {
defaultResponses.add("Could you elaborate on that?");
}
}
/**
* Randomly select and return one of the default responses.
* @return A random default response
*/
private String pickDefaultResponse()
{
// Pick a random number for the index in the default response list.
// The number will be between 0 (inclusive) and the size of the list (exclusive).
int index = randomGenerator.nextInt(defaultResponses.size());
return defaultResponses.get(index);
}
}

View File

@@ -0,0 +1,10 @@
#BlueJ class context
comment0.target=SupportSystem()
comment0.text=\nCreates\ a\ technical\ support\ system.\n\n
comment1.target=void\ start()
comment1.text=\nStart\ the\ technical\ support\ system.\ This\ will\ print\ a\ welcome\ message\ and\ enter\ninto\ a\ dialog\ with\ the\ user,\ until\ the\ user\ ends\ the\ dialog.\n\n
comment2.target=void\ printWelcome()
comment2.text=\nPrint\ a\ welcome\ message\ to\ the\ screen.\n\n
comment3.target=void\ printGoodbye()
comment3.text=\nPrint\ a\ good-bye\ message\ to\ the\ screen.\n\n
numComments=4

View File

@@ -0,0 +1,74 @@
import java.util.HashSet;
/**
* This class implements a technical support system.
* It is the top level class in this project.
* The support system communicates via text input/output
* in the text terminal.
*
* This class uses an object of class InputReader to read input
* from the user, and an object of class Responder to generate responses.
* It contains a loop that repeatedly reads input and generates
* output until the users wants to leave.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class SupportSystem
{
private InputReader reader;
private Responder responder;
/**
* Creates a technical support system.
*/
public SupportSystem()
{
reader = new InputReader();
responder = new Responder();
}
/**
* Start the technical support system. This will print a welcome message and enter
* into a dialog with the user, until the user ends the dialog.
*/
public void start()
{
boolean finished = false;
printWelcome();
while(!finished) {
HashSet<String> input = reader.getInput();
if(input.contains("bye")) {
finished = true;
}
else {
String response = responder.generateResponse(input);
System.out.println(response);
}
}
printGoodbye();
}
/**
* Print a welcome message to the screen.
*/
private void printWelcome()
{
System.out.println("Welcome to the DodgySoft Technical Support System.");
System.out.println();
System.out.println("Please tell us about your problem.");
System.out.println("We will assist you with any problem you might have.");
System.out.println("Please type 'bye' to exit our system.");
}
/**
* Print a good-bye message to the screen.
*/
private void printGoodbye()
{
System.out.println("Nice talking to you. Bye...");
}
}

View File

@@ -0,0 +1,9 @@
That sounds odd. Could you describe that problem in more detail?
No other customer has ever complained about this before. What is your system configuration?
That sounds interesting. Tell me more...
I need a bit more information on that.
Have you checked that you do not have a dll conflict?
That is explained in the manual. Have you read the manual?
Your description is a bit wishy-washy. Have you got an expert there with you who could describe this more precisely?
That's not a bug, it's a feature!
Could you elaborate on that?

Some files were not shown because too many files have changed in this diff Show More