first commit

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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