/*
           +-----------+                
           | ILoObject |<------------+
           +-----------+             |
           +-----------+             |
                / \                  |
                ---                  |
                 |                   |
       -------------------           |
       |                 |           |
+------------+    +----------------+ |
| MTLoObject |    | ConsLoObject   | |
+------------+    +----------------+ |
+------------+  +-| Object first   | |
                | | ILoObject rest |-+
                | +----------------+ 
                |
                |   +--------------------------+
                |   | interface ISame          |
                |   +--------------------------+
                |   | boolean same(Object obj) |
                |   +--------------------------+
                |             / \
                |              |
                |    - - - - - - 
                v    |
          +------------+
          | Object     |
          +------------+
          | Type ???   |
          | ...        |
          +------------+
*/

interface ISame{
  // is this the same as the given object?
  boolean same(Object obj);
}

interface Traversal{
  // are there any elements in this data set?
  boolean hasMore();

  // produce the current first element in this data set
  Object current();

  // produce a traversal for the remainder of this data set
  Traversal advance();
}

interface ISelect{
  // does the given object have the desired property?
  boolean choose(Object obj);
}

class Book{
  String title;
  String author;
  
  Book(String title, String author){
    this.title = title;
    this.author = author;
  }
}

// selector for books written by Shakespeare
class ByShakespeare implements ISelect{
  // is the given book written by Shakespeare?
  boolean choose(Object obj){
    return ((Book)obj).author.equals("Shakespeare");
  }
}

// selector for books with the given title
class ByTitle implements ISelect{
  String title;
  ByTitle(String title){
    this.title = title;
  }

  // does the given book have this title
  boolean choose(Object obj){
    return ((Book)obj).title.equals(this.title);
  }
}

interface ILoObject extends ISame, Traversal{
  // to compute the size of this list
  int size();

  // is the given book in this list?
  boolean contains (Object that);

  // is this the same as the given object?
  boolean same(Object obj);
}

class MTLoObject implements ILoObject{
  MTLoObject() {}

  int size(){ return 0; }

  boolean contains (Object that){
    return false;
  }

  // is this the same as the given object?
  boolean same(Object obj){ return obj instanceof MTLoObject; }

  // are there any elements in this data set?
  boolean hasMore(){ return false; }

  // produce the current first element in this data set
  Object current(){ 
    // should throw an IllegalOperationException
    return new Error("no current in en empty data set"); 
  }

  // produce a traversal for the remainder of this data set
  Traversal advance(){ 
    // should throw an IllegalOperationException
    return this; 
  }
}

class ConsLoObject implements ILoObject{
  Object first;
  ILoObject rest;

  ConsLoObject(Object first, ILoObject rest){
    this.first = first;
    this.rest = rest;
  }

/* TEMPLATE:
... this.first ...                   -- Object
... this.rest ...                    -- ILoObject
... this.rest.size() ...             -- int
... this.rest.contains( Object ) ... -- boolean
*/
  int size(){ 
    return 1 + this.rest.size(); 
  }

  boolean contains (Object that){
    return ((ISame)this.first).same(that)
        || this.rest.contains(that);
  }

  // is this the same as the given object?
  boolean same(Object obj){ 
    if (obj instanceof ConsLoObject)
      return this.sameConsLoObject(((ConsLoObject)obj));
    else 
      return false;
  }

  // is this the same as the given ConsLoObject?
  boolean sameConsLoObject(ConsLoObject that){ 
    return ((ISame)this.first).same(that.first) &&
           this.rest.same(that.rest);
  }

  // are there any elements in this data set?
  boolean hasMore(){ return true; }

  // produce the current first element in this data set
  Object current(){ 
    return this.first; 
  }

  // produce a traversal for the remainder of this data set
  Traversal advance(){ 
    return this.rest; 
  }
}

class Error{
  String message;

  Error(String message){
    this.message = message;
  }
}
class Examples{
  Examples () {}

  Book b1 = new Book("Tortilla Flat", "Steinbeck");
  Book b2 = new Book("Pearl", "Steinbeck");
  Book b3 = new Book("Othello", "Shakespeare");

  ILoObject mt = new MTLoObject();
  ILoObject shorty = new ConsLoObject(this.b1,
                     new ConsLoObject(this.b2, this.mt));
  ILoObject list = new ConsLoObject(this.b3, this.shorty);

  ByShakespeare byS = new ByShakespeare();
  
  // does the collection of data generated by the given traversal
  // contain an object specified by the given ISelect selector?
  boolean contains(Traversal tr, ISelect select){
    if (tr.hasMore()){
      // non-empty clause 
      if (select.choose(tr.current()))
        return true;
      else
        return this.contains(tr.advance(), select);
    }
    else
      // empty clause
      return false;
  }
  
  /* TEMPLATE:
  ... tr.hasMore() ...       -- boolean
      if == false - nothing more available
      if == true:
        ... tr.current() ...                  -- Object
        ... tr.advance() ...                  -- Traversal
        ... select.choose(Object) ...         -- boolean
        ... contains(Traversal, select) ...   -- boolean
  */  


  // tests for the method contains:
  boolean testContains1 = this.contains(mt, byS) == false;
  boolean testContains2 = this.contains(shorty, byS) == false;
  boolean testContains3 = this.contains(list, byS) == true;

  boolean testContains4 = this.contains(mt, new ByTitle("Pearl")) == false;
  boolean testContains5 = this.contains(shorty, new ByTitle("Othello")) == false;
  boolean testContains6 = this.contains(list, new ByTitle("Othello")) == true;

}


 
