package lectures.inheritance;
import util.annotations.WebDocuments;
/**
 * This class illustrates the concept of sets, method overriding.
 */
@WebDocuments({"Lectures/Inheritance.pptx", "Lectures/Inheritance.pdf", "Videos/Inheritance.avi"})
public class AnInheritingStringSet extends AnInheritingStringDatabase // extending an extended class
    // comment out the next line when asked
    implements InheritingStringDatabase // same interface as implemented by the superclass, need not be explicitly mentioned
    { 
/*
 * Since AnInheritingStringSet extends AnInheritingStringDatabase,
 * and AnInheritingStringDatabase extends ABaseStringHistory,
 * AnInheritingStringSet is a direct/child/immediate subclass/subtype of AnInheritingStringDatabase
 * an indirect/descendant subclass/subtype of ABaseStringHistory, and a subclass/subytype
 * of both AnInheritingStringDatabase and ABaseStringHistory.
 * 
 * 
 * Similarly, AnInheritingStringDatabase is a direct/parent/immediate superclass of 
 * AnInheritingStringSet,  ABaseStringHistory is an indirect/ancestor superclass
 * of AnInheritingStringSet, both AnInheritingStringDatabase and ABaseStringHistory
 * are superclasses/supertypes of AnInheritingStringSet, and AnInheritingStringDatabase
 * is the closer superclass.
 *   
 * 
 * 
 * If class A extends B, and B is a subclass of C then A is a:
 *   (a) subclass of B
 *   (b) direct/child subtype of B.
 *   (c) direct/child subtype of C.
 *   (d) indirect/descendant subtype of B.
 *   (e) indirect/descendant subtype of C.
 *   (f) closer subclass of C (in comparison to B)
 *
 */
/*
 * Look at the interface of this class:
 * 
 * (T/F) If class C1 implementing I1 is a subclass of C2 implementing I2, then I2
 * must be a subtype of I1. 
 */
    
    public AnInheritingStringSet() {
        super(); 
//      super.super();
        uselessVariable = 1;
//      System.out.println("AnInheritingStringSet constructor called");
//      super();    
    }
    
    @Override
    public void addElement(String element) {
        /*
         * Set break point below when asked.
         */
        if (isFull()) {
            System.out.println("Adding item to a full history");
        } else if (!member(element)) { // add only if the element is not a member
            
            super.addElement(element);
            /*
             * what if we called addElement without super?           * 
             */
//          addElement(element); 
        }
    }
/*
 *
 * Click on the addElement in super.addElement() and hit Command/CTRL-Click to see 
 * where the call on super is defined.
 *
 * super.addElement() in AnInheritingStringSet refers to an addElement() method 
 * in:
 *    (a)AnInheritingStringSet
 *    (b)the direct/parent superclass of AnInheritingStringSet.
 *    (c)an indirect/ancestor superclass of AnInheritingStringSet.
 * 
 * The addElement() methods in AnInheritingStringSet and ABaseStringHistory
 * have the same:
 *     (a) name
 *     (b) parameter(s)
 *     (c) header
 * 
 * (T/F) A class can implement two methods with the same header.
 * (T/F) A class and a superclass can implement two methods with the same header.
 * 
 * Uncomment the addElement() method in the direct superclsas of this class.
 * 
 * Click again on the addElement in super.addElement() and hit Command/CTRL-Click to see 
 * where the call on super is defined.
 *
 * Comment out the method again in the direct superclass. 
 * 
 * If a method with header m is implemented in class A, its direct super class
 * B, and its indirect super class, C, then call to super.m() in A calls the
 * implementation of m() in:
 *     (c) A
 *     (b) B
 *     (c) C
 *   
 * (T/F) The call super.m() in class C refers to the implementation of m in the:
 *    (a) closest superclass of C that implements m.
 *    (b) furthest superclass of C that implements m.
 */
    
     public static void main (String[] args) {       
        InheritingStringDatabase aStringSet = new AnInheritingStringSet();
        System.out.println (aStringSet.size()); // size() is implemented in ABaseStringHistory
   
//      StringDatabase aStringSet = new AStringDatabase(); 
        /*
         * click on manipulateDatabase and hit F3/CTRL/CMD Click to see where 
         * the call is defined
         */
        /*
         * set break point
         */
        manipulateDatabase(aStringSet); 
        /*
         * What if we called the static manipulateDatabase with "super"? 
         * Is that legal in Java? Try uncommenting this to see.
         */
//      super.manipulateDatabase(aStringSet);
      }  
}
/*
 * INHERITING ANCESTOR METHODS
 *  
 * Based  on the call to size() in main:
 * 
 * If object, O, is an instance of C, then it is possible to invoke on O public 
 * methods declared in:
 *   (a) C,
 *   (b) C's direct superclass
 *   (c) C's indirect superclasses
 *   
 */
/* 
 * 
 * SETS
 * 
 * Run the main method, look at the output, and study the addElement() method
 * defined here.
 * 
 * Adding an item a second time to an instance of AnInheritingStringDatabase
 * results in:  
 *  (a) an error.
 *  (b) the addition being ignored.
 *  (c) a duplicate item. 
 * 
 * Adding an item a second time to an instance of AnInheritingStringSet
 * results in:  
 *  (a) an error.
 *  (b) the addition being ignored.
 *  (c) a duplicate item.
 */
/*
 *  REDECLARING ANCESTOR METHODS
 *  
 * Set the break point in main and use Step Into (F5) and Step Over (F6) to see 
 * which path is taken when main is called for each added element.
 * 
 * If O is an instance of A, and mutltiple implementations of m() exist in the super type
 * hierarchy, then calling m() on O:
 * 
 * (a) gives a compile time error.
 * (b) gives a runtime error.
 * (c) calls all implementations of m() - the one in A and the ones in A's superclasses.
 * (d) calls the implementation of m() in the furthest superclass of A.
 * (e) calls the implementation of m() in A, if it exists, or the 
 * implementation of m() in the closest superclass of A
 * 
 * (T/F) A subclass can control the nature of inheritance by overriding an
 * inherited method, that is, replacing the implementation of an inherited with
 * its own one.
 * 
 * 
 * If O is an instance of A, and a method with header m() is implemented in
 * A and one or more of its superclasses, then it *not* possible for a method in A to call the
 * implementation of m() in:
 * 
 * (a) A
 * (b) any superclass of A.
 * (c) none of the above
 * 
 * 
 * Comment out the implements clause in this class. Does the line:
 * 
 *    InheritingStringDatabase aStringSet = new AnInheritingStringSet();
 *    
 *  in the main method give an error?
 *  
 * (T/F) A class is implicitly considered to implement the interfaces implemented by its superclasses.
 * (T/F) A subclass can control the nature of inheritance by disabling the invocation
 *  of an inherited method declared in an interface of a superclass.
 * 
 */

/*
 * ROOT SUPERCLASS
 * 
 * Go to the definition of toString() in ABaseStringHistory.
 * 
 * Comment out the current return statement and uncomment the one below it,
 * which uses super.toString().
 * 
 * ABaseStringHistory does not explicitly extend any class.
 *  
 * To which class is super referring?
 * 
 * Set a breakpoint at the new return statement.
 * 
 * Debug-Run the program again. When you stop at the break point,
 * and step into (F5) the super call in the return statement.
 * 
 * In which class do you land? (You may not be able to see the source code of 
 * the class if you do not have a jdk installed, but you should be able
 * to see the name of the class in Eclipse.) 
 * 
 * Stepping into super.toString() in ABaseStringHistory
 *   (a) gives an error
 *   (b) executes the toString() method in class Object
 *   (c) does nothing
 *   
 * (T/F) The method toString() is implemented in the class Object.
 * 
 * A class that has no superclass is a root superclass.
 * 
 * Which is true:
 *  (a) The predefined class, Object, is the immediate superclass of any class
 *          that does not explicitly extend a class. 
 *  (c) A class that does not explicitly extend any class is a root superclass.
 * 
 * 
 * Look at the stack of calls in the Debug window. 
 * 
 * You can keep doing step-return (F7) until you hit the main class.
 * 
 * Which statement in the main class led to the call of toString()? (If you were doing
 * step-return the whole way, you will land at the line following that statement)
 * 
 * System.out.println(o) calls 
 *    the toString() method on o. 
 *    the println() method on o.
 *    getter() methods of o.
 *    none of the above
 * 
 * Comment out the super.toString() call and uncomment the original when you are done.
 * 
 */
/*
 * 
 * REDECLARING VARIABLES
 * 
 * Uncomment the declarations of the "size" variable in AnInheritingStringDatabase.
 * 
 * Now we have two declarations of this variable, one in AnInheritingStringDatabase
 * and one in its superclass.
 * 
 * Run that program again and look at the output.
 * 
 * Redeclaring the size variable in AnInheritingStringDatabase causes the following
 * methods to not work:
 *    a) member()
 *    b) removeElement()
 *    c) addElement()
 *    d) none of the above.
 *
 * Set a break point at the start of the addElement() method in ABaseStringHistory.
 * 
 * Set a breakpoint also on at the start of the shiftUp method in AnInheritingStringDatabase.

 * When the program stops at the addElement() method, hover over the 
 * value of the size variable referenced in the method.
 * 
 * What value does it have when the method finishes?
 * 
 * (T/F) At the end of the addElement() method of ABaseStringHistory, the 
 * value of the size variable referenced by the method is 0.  
 * 
 * Press F8 to resume the program, until you hit the breakpoint in shiftUp().
 * 
 * When the program stops in the shiftUp() method, hover over the size
 * variable in the method body. 
 * 
 * What value is it at the start of  the method? 
 * 
 * (T/F) At the start of the shiftUp() method of AnInheritingStringDataabsese the
 * value of the size variable referenced by the method is  0 when 
 * AnInheritingStringDatase redeclares the variable.
 * 
 * Look at the variables window in the debugger
 * at the variables of the instance of AnInheritingStringDatabase (under "this").
 * 
 * How many size variables does it have and what are their values? 
 * 
 *  The physical structure of AnInheritingStringDatabase has:
 *  (a) only one size variable,
 *  (b) two size variables, whose values are always the same.
 *  (c) two size variables, whose values can diverge.
 *  
 *  (T/F) The physical structure of an object cannot have
 *  two variables with the same name.
 *  
 *  (T/F) Methods declared in class C can refer to variables declared in its subclasses.
 *  
 *  
 *  
 * Comment out the duplicate size variable declaration.   
 *
 * Will you ever re-declare a variable again in a subclass?
 * 
 */
/*
 * PRIVATE VS PROTECTED VS DEFAULT ACCESS
 *
 * For this part, you need to know to look for compile errors in classes
 * in this package and the package lectures.inheritance.extra.  
 * 
 * You will play with the access of the variable "uselessVariable" declared in
 * class ABaseStringHistory by commenting and uncommenting appropriate
 * alternate declarations.
 * 
 * In reaction to changes to this access, you will determine in which classes
 * (in these two packages) compile errors arise, indicating that the variable
 * is no longer visible.
 * 
 * You can go to the declaration of uselessVariable and press CTRL SHIFT G
 * to see all of its references, or place your cursor in it, right-click and
 * select References->Workspace.
 * 
 * The variable is accessed in the several places. It is accessed in the three
 * classes we have seen so far, all of which are in this package. 
 * 
 * It is also accessed in AnIndependentClassInSamePackage, declared in this
 * package, which is not a subclass of ABaseStringHistory.
 * 
 * In addition, it is accessed in 
 * lectures.inheritance.extra.AnObservableStringHistory and
 * lectures.inheritace.extra.AnIndependentClassInAnotherPackage. The former is 
 * a subclass of AABaseStringHistory while the latter is not.
 * 
 * Currently the access is public, which makes the variable visible in all
 * classes in which it is accessed. 
 * 
 * Change the access to private, protected, and default, and observe the errors.
 * After each change, save the file so that Java rebuilds the project and you see
 * the errors.
 * 
 * A class C can access private variables of class D only if: 
 *  (a) C and D are the same.
 *  (b) C is a subclass of D (directly or indirectly).
 *  (c) C is in the same package as D.
 *  (d) none of the above
 *  
 * A class C can access protected variables of class D only if:  
 *  (a) C is a subclass of D (directly or indirectly).
 *  (b) C is in the same package as D.
 *  (c) C is a subclass of D (directly or indirectly) or C is in
 *      the same package as D
 *  (d) C and D are the same
 *  
 * A class C can access variables declared in class D with default access only if:  
 *  (a) C and D are the same.
 *  (b) C is a subclass of D (directly or indirectly).
 *  (c) C is in the same package as D.
 *  (d) C is a subclass of D (directly or indirectly) or C is in
 *      the same package as D.
 *  
 * Return the variable to public access once you're done.
 */
/*
 * CONSTRUCTORS AND SUBCLASSING
 * 
 * Uncomment the println() calls in the parameterless constructor in 
 * ABaseStringHistory, AnInheritingStringDatabase, and AnInheritingStringSet
 * and run this program and observe the output.
 * 
 * If C is a subclass of E, and an object c of type C is instantiated, then:
 *  (a) a constructor of only C is called.
 *  (b) a constructor of only E is called.
 *  (c) a constructor of C and E are called. 
 * 
 * Comment out the first call to super() and uncomment the first call to
 * super(2) in the constructor of AnInheritingStringDatabase.
 * 
 * Run the program or use F3 to go to the definition of super(2).
 * 
 * (T/F) A constructor in a class can determine which overloaded
 *  constructor of its superclass is called by providing appropriate parameters to
 *  a super call.
 *  
 *  Comment out the first call to super(2) and uncomment the second one. 
 *  What happens?
 *  
 *  Comment out the super(2) call.
 *  
 * (T/F) A super() call can be invoked anywhere in a constructor.
 * 
 * Uncomment the super(2) call in the clear() method. What happens? 
 * 
 * (T/F) A superclass constructor can be called from any method of a subclass.
 * 
 * Comment out this super(2) call.
 * 
 * Now you have no super calls in AnInheritingStringDatabase.
 * 
 * Run the program.
 * 
 * Is the constructor of ABaseStringHistory called? If so, which one?
 * 
 * Comment out the parameterless constructor in ABaseStringHistory and go to
 * AnInheritingStringDatabase. What happened?
 * 
 * If no super() call is made in a constructor:
 *  (a) an exception is thrown.
 *  (b) no superclass constructor is called.
 *      (c) a call to the parameterless super constructor is automatically added as
 *  the first statement in the constructor. 
 *  
 *  Uncomment the parameterless constructor in ABaseStringHistory.
 *  
 * Comment out the entire constructor in AnInheritingStringDatabase so
 * we have no constructor in this class.
 * 
 * Run the program. Is the superclass constructor called?
 * 
 * (T/F) If a class has no constructor, then Java inserts into its object code
 * a constructor with no parameters that makes a call to a constructor with
 * no parameters in its superclass.
 */
/*
 * 
 * ORDER OF INITIALIZATION
 * 
 * To really understand what is going on, put a break point on the first statement of 
 * the constructor in AStringSet and use F5 (Step into) to trace the path to the constructor
 * in ABaseStringHistory noting the order in which the variables and constructors are called.
 * 
 * Step over (F6) any println calls so that you do not get pulled into that code (if you
 * accidentally step into a println call, just step return- F8).
 * 
 * if you reach class Object, Step return (F7)
 * 
 * If you reach an initializing variable declaration such as 
 *       int uselessVariable2 = 0;
 * Step over (F6)
 * 
 * Which are true:
 * 
 * The initialization declaration of AnInheritingStringDatabase
 *  AnInheritingStringDatabase is executed:
 *     (a) before the initializing declarations in 
 *          ABaseStringHistory is executed.
 *     (b) before the constructor of  AnInheritingStringDatabase is executed
 *     (c) before the constructor of ABaseStringHistory is executed.  
 * 
 * (T/F) Java executes the constructor of a class after before executing the constructors of its superclasses.
 * (T/F) Java executes the initialization declaration of a class before executing its constructors.
 * (T/F) Java executes the initialization declaration of a class before executing the initialization
 * declarations of its superclassses.
 * (T/F) Java executes the initialization declaration of a class before executing the constructors of its superclasses.
 * 
 * Look carefully at the initialization declarations of anotherUselessVariable
 * 
 * (T/F) The initialization declaration of a class can refer to a variable
 * declared in a superclass.
 * 
 * (T/F) The initialization declaration of a class should be executed after the variables of its
 * superclasses have been initialized by the superclass declarations or constructors.
 */
/*
 * Next class StringHistoryFactory
 */