Problem Set 1: Programming With Java 6.005 Elements of Software Construction | Spring 2011 Problem Set 1: Programming With Java Due: Monday, February 14, 2011 at 11:59pm The purpose of this problem set is to give you practice programming Java programs. You'll debug buggy code and implement a specification. Pull out the problem set code from SVN admin. Problem 0: Warm Up An object from a class StringToWords (available in StringToWords.java) produces a sequence of strings, one in each call to next(). The client may only call next() if hasNext() returns true. 1. [10 points] Your task is to implement the methods next() and hasNext() in the class StringToWords (found in ps1.warmup). Its constructor takes a String as argument, and next() should return the sequence of space-separated words in the string. Assume that behavior is undefined for more than one space characters between words, and non-space whitespace characters (like tab or newline) are not considered spaces. For example, this program: public class StringToWordsTester {
public static void main(String args[]) {
StringToWords stw = new StringToWords(" This is 6.005. ");
while (stw.hasNext()) {
System.out.println("<"+stw.next()+">");
}
}
} should print <6.005.> StringToWordsTester is also found in ps1.warmup. Problem 1: Debugging Ben Bitdiddle, eager to test his new Java skills, created a Java class that models people. You can find his code in the package ps1.debug. His Person class is displayed below: public class Person {
private String name;
public Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public boolean hasSameName(Person person){
return person.name == this.name;
}
@Override
public String toString(){
return "[Name: " + this.name + "]";
}
} Ben decided that he should be the only person named Ben so he wrote a class, RemovePeople.java, that removes all Person objects that have the name "Ben." That code is also found in ps1.debug but is reprinted below: public class RemovePeople {
public static void main(String[] args) {
Person ben = new Person("Ben");
ArrayList persons = new ArrayList(); persons.add(new Person(new String("Ben"))); persons.add(new Person(new String("Alyssa"))); persons.add(new Person(new String("Alice"))); for (Person person : persons){ if (person.hasSameName(ben)) { persons.remove(person); } } System.out.println(persons); } } 1. [5 Points] Unfortunately, this code does not seem to remove people who have the name Ben. Allysa P. Hacker suggested that there may be something wrong with Ben's hasSameName method in Person. Fix that method. 2. [15 Points] After listening to Allysa's advice Ben ran the code again, but now it throws an exception. What exception is thrown? Why is this happening? Fix the code in RemovePeople.java. Now that his Person code works, Ben decided he wanted to make a subclass of Person called Student, which looks like this (and is available in Student.java in ps1.debug):
public class Student extends Person {
private String grade;
private String daysAttended;
public Student(String name, String grade, String daysAttended) {
super(name);
this.grade = grade;
this.daysAttended = daysAttended;
}
public void inflateGrade(){
this.grade.replace("-", "+");
}
public void boostAttendance(){
this.daysAttended += 2;
}
@Override
public String toString(){
return "[Name: " + this.getName() + ", Grade: "+this.grade+ ", Days attended; " + this.daysAttended + "]";
}
}
The Student class adds a field called grade. A grade is a String that has a letter possibly followed by a “+” or “-” such as “B-” or “A”. Ben was feeling magnanimous so he wrote a grade inflation function that changes all “-”s to “+”s. So a "C-" would become a "C+" but an "A" would stay an "A" 3. [10 Points] Unfortunately, the inflateGrade method does not actually seem to change the grade variable. Fix Ben's method and run InflateGrades.java to confirm Ben's code now works. The Student class also adds a field called daysAttended, which is a String that tracks how many days of class a student attends throughout the semester. Ben also decided to add a convenient way to improve a student's attendance, boostAttendance(), which simply increases the number of days a student attended by 2. 4. [10 Points] Sadly, boostAttendance does not appear to have the proper behavior either. Fix the problem by editing the boostAttendance method of Student. Do not change the type of daysAttended. Now run InflateGrades.java to confirm that the problem is fixed. Problem 2: Extending Functionality In this problem, we'll add some functionality to code we've seen in lecture already. Recall in lecture that a simple cache was presented.
public class Cache {
private Page[] cache = new Page[100]; // contains Pages and null references
private int cachePointer = 0; // index of next slot available for a page
/*
* Returns a cached Page object p such that p.getURL() is url.
* Returns null if no page for url found in the cache.
*/
public Page get(URL url) {
for (Page p : cache) {
if (p == null) continue;
else if (p.getURL().equals(url)) return p;
}
return null;
}
/*
* Store page in the cache.
*/
public void put(Page page) {
cache[cachePointer] = page;
++cachePointer;
if (cachePointer >= cache.length) cachePointer = 0;
}
}
public class Page {
// fields
private URL url;
private String content;
private static Cache cache = new Cache();
// constructor
/**
* Make a new Page for a URL, downloading it immediately. Throws IOException
* if an error occurs accessing the web server.
*/
public Page(URL url) throws IOException {
this.url = url;
if (download()) {
cache.put(this);
}
}
…
}
One obvious problem is that since web pages may change often, for certain usage patterns, our cache will keep an outdated copy of a given webpage for an extended period of time. One way to fix this problem is to create an alternate constructor for Page that takes an additional argument as follows:
public Page(URL url, bool refresh) throws IOException {
...
}
If refresh is false, this alternate constructor should behave identically to the original constructor. If refresh is true, the constructor should fetch a new copy of the URL, and update the cache with the new content (potentially discarding the stale copy, if present). 1. [15 points] Fill in the implementation of the new Page constructor, as described above. In addition to the alternate Page constructor, we wish to also add a method to the Cache class that forcefully refreshes every page in the cache. That is, for each Page currently present in the array of Pages, try to update the Page's content with a fresh version by calling fetch again.
public void refreshCache() {
...
}
2. [15 points] Fill in the implementation of refreshCache() as described above. In particular, note that refreshCache() does not have the potential of throwing an IOException. If a page exists in the cache and calling fetch results in an IOException being thrown, refreshCache should remove that Page from the cache. Run CacheTest to test your implementation for parts 1 and 2. CacheTest should print "Success!" if your implementations are complete. NOTE: CacheTest may not be a full test of the implementation as specified; passing it is necessary to get full credit, but is not a guarantee. You should scrutinize your implementation and write supplemental tests, if necessary. Below is the Weather extension that was introduced in lecture. It is a subclass of Page that is specialized to retrieve the weather.
public class Weather extends Page {
/**
* Makes a Weather object for a US zipcode.
* Requires zipcode to be a valid 5-digit zipcode.
*/
public Weather(String zipcode) throws IOException {
super(new URL("http://weather.yahooapis.com/forecastrss?p=" + zipcode));
}
@Override
protected boolean download() throws IOException {
boolean cacheMiss = super.download();
if (cacheMiss) {
String line = Match.between(this.getContent(),
"");
this.condition = Match.between(line, "text=\"", "\"");
this.temperature = Integer.valueOf(Match.between(line, "temp=\"",
"\""));
} else {
Page p = getCache().get(getURL());
if (p instanceof Weather) {
Weather w = (Weather) p;
this.condition = w.condition;
this.temperature = w.temperature;
}
}
return cacheMiss;
}
public String getCondition() {
return condition;
}
public int getTemperature() {
return temperature;
}
}
public class WeatherTest {
public static void main(Strin[] args) {
downloadPage("http://www.google.com");
downloadPage("http://www.google.com");
downloadPage("http://weather.yahooapis.com/forecastrss?p=02139");
downloadWeather("02139");
}
}
3. [20 points] Above is the Weather Class we saw in lecture. We have seen that the downloadWeather("02139") call prints null. Your job in this part is to fix this problem. Run WeatherTest to verify that your fix is correct. In particular, the weather in 02139 should be present.