first commit
This commit is contained in:
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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");
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -0,0 +1,4 @@
|
||||
chris
|
||||
08459 700000
|
||||
address 7
|
||||
|
Binary file not shown.
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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.";
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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.
|
@@ -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
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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...");
|
||||
}
|
||||
}
|
@@ -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
Reference in New Issue
Block a user