/* This is wrapper class...
 Objective would be to push more functionality into this Class to enforce consistent definition
 */
public abstract class Generics {
	public final String masterType = "Generic";
	private String type;	// extender should define their data type

	// generic enumerated interface
	public interface KeyTypes {
		String name();
	}
	protected abstract KeyTypes getKey();  	// this method helps force usage of KeyTypes

	// getter
	public String getMasterType() {
		return masterType;
	}

	// getter
	public String getType() {
		return type;
	}

	// setter
	public void setType(String type) {
		this.type = type;
	}
	
	// this method is used to establish key order
	public abstract String toString();

	// static print method used by extended classes
	public static void print(Generics[] objs) {
		// print 'Object' properties
		System.out.println(objs.getClass() + " " + objs.length);

		// print 'Generics' properties
		if (objs.length > 0) {
			Generics obj = objs[0];	// Look at properties of 1st element
			System.out.println(
					obj.getMasterType() + ": " + 
					obj.getType() +
					" listed by " +
					obj.getKey());
		}

		// print "Generics: Objects'
		for(Object o : objs)	// observe that type is Opaque
			System.out.println(o);

		System.out.println();
	}
}
/*
 * FRQ class extends Generics and defines abstract methods
 */
public class FRQ extends Generics {
	// Class data
	public static KeyTypes key = KeyType.title;  // static initializer
	public static void setOrder(KeyTypes key) { FRQ.key = key; }
	public enum KeyType implements KeyTypes {title, year, problem, subject, difficulty}

	// Instance data
	private final int year;
	private final int problem;
	private final String subject;
	private final String difficulty;

	/* constructor
	 *
	 */
	public FRQ(int year, int problem, String subject, String difficulty)
	{
		super.setType("FRQ");
		this.year = year;
		this.problem = problem;
		this.subject = subject;
		this.difficulty = difficulty;
	}

	/* 'Generics' requires getKey to help enforce KeyTypes usage */
	@Override
	protected KeyTypes getKey() { return FRQ.key; }
	
	/* 'Generics' requires toString override
	 * toString provides data based off of Static Key setting
	 */
	@Override
	public String toString()
	{
		String output="";
		if (KeyType.year.equals(this.getKey())) {
			output += this.year;
		} else if (KeyType.problem.equals(this.getKey())) {
			output += this.problem;
		} else if (KeyType.subject.equals(this.getKey())) {
			output += this.subject;
		} else if (KeyType.difficulty.equals(this.getKey())) {
			output += this.difficulty;
		} else {
			output += super.getType() + ": " + this.year + ", " + this.problem + ", " + this.subject + ", " + this.difficulty;
		}
		return output;
		
	}

	// Test data initializer
	public static FRQ[] FRQs() {
		return new FRQ[]{
				new FRQ(2010, 1, "Methods and Control Structures", "Medium"),
				new FRQ(2017, 4,  "2D Arrays", "Easy"), 
				new FRQ(2021, 3, "Classes", "Hard"),
				new FRQ(2015, 1, "Arrays", "Easy"),
		};
	}
	
	/* main to test FRQ class
	 * 
	 */
	public static void main(String[] args)
	{
		// Inheritance Hierarchy
		FRQ[] objs = FRQs();

		// print with title
		FRQ.setOrder(KeyType.title);
		FRQ.print(objs);

		// print subject only
		FRQ.setOrder(KeyType.subject);
		FRQ.print(objs);
	}

}
FRQ.main(null);
class [LREPL.$JShell$22I$FRQ; 4
Generic: FRQ listed by title
FRQ: 2010, 1, Methods and Control Structures, Medium
FRQ: 2017, 4, 2D Arrays, Easy
FRQ: 2021, 3, Classes, Hard
FRQ: 2015, 1, Arrays, Easy

class [LREPL.$JShell$22I$FRQ; 4
Generic: FRQ listed by subject
Methods and Control Structures
2D Arrays
Classes
Arrays

public class FRQTypes extends Generics {
	// Class data
	public static KeyTypes key = KeyType.title;  // static initializer
	public static void setOrder(KeyTypes key) { FRQTypes.key = key; }
	public enum KeyType implements KeyTypes {title, name, year, qnum, description}

	// Instance data
	private final String name;
	private final int year;
	private final int qnum;
    private String description;

	/* constructor
	 *
	 */
	public FRQTypes(String name, int year, int qnum, String description)
	{
		super.setType("FRQTypes");
		this.name = name;
		this.year = year;
		this.qnum = qnum;
        if (description == null) {
            this.description = "No description";
        }
        else {
            this.description = description;
        }
	}

	/* 'Generics' requires getKey to help enforce KeyTypes usage */
	@Override
	protected KeyTypes getKey() { return FRQTypes.key; }
	
	/* 'Generics' requires toString override
	 * toString provides data based off of Static Key setting
	 */
	@Override
	public String toString()
	{
		String output="";
		if (KeyType.name.equals(this.getKey())) {
			output += this.name;
		} else if (KeyType.year.equals(this.getKey())) {
			output += this.year;
		} else if (KeyType.qnum.equals(this.getKey())) {
			output += this.qnum;
		} else if (KeyType.description.equals(this.getKey())) {
			output += this.description;
		} else {
			output += super.getType() + ": " + this.name + ", " + this.year + ", " + this.qnum + ", " + this.description;
		}
		return output;
		
	}

	// Test data initializer
	public static FRQTypes[] FRQTypess() {
		return new FRQTypes[]{
				new FRQTypes("Lightboard", 2019, 4, "Creating a lightboard with methods and classes."),
                new FRQTypes("Steptracker", 2019, 2, "Creating a fitness tracking system, writing a class with methods.")
		};
	}
	
	/* main to test FRQTypes class
	 * 
	 */
	public static void main(String[] args)
	{
		// Inheritance Hierarchy
		FRQTypes[] objs = FRQTypess();

		// print with title
		FRQTypes.setOrder(KeyType.title);
		FRQTypes.print(objs);

		// print name only
		FRQTypes.setOrder(KeyType.name);
		FRQTypes.print(objs);
	}

}

FRQTypes.main(null);
class [LREPL.$JShell$12C$FRQTypes; 2
Generic: FRQTypes listed by title
FRQTypes: Lightboard, 2019, 4, Creating a lightboard with methods and classes.
FRQTypes: Steptracker, 2019, 2, Creating a fitness tracking system, writing a class with methods.

class [LREPL.$JShell$12C$FRQTypes; 2
Generic: FRQTypes listed by name
Lightboard
Steptracker

public class User extends Generics {
	// Class data
	public static KeyTypes key = KeyType.title;  // static initializer
	public static void setOrder(KeyTypes key) { User.key = key; }
	public enum KeyType implements KeyTypes {title, uid, password, name, dob}

	// Instance data
    private final String uid;  // user / person id
    private final String password;
    private final String name;
    private final int dob;

	/* constructor
	 *
	 */
	public User(String uid, String password, String name, int dob)
	{
		super.setType("User");
		this.uid = uid;
		this.password = password;
		this.name = name;
        this.dob = dob;
	}

	/* 'Generics' requires getKey to help enforce KeyTypes usage */
	@Override
	protected KeyTypes getKey() { return User.key; }
	
	/* 'Generics' requires toString override
	 * toString provides data based off of Static Key setting
	 */
	@Override
	public String toString()
	{
		String output="";
		if (KeyType.uid.equals(this.getKey())) {
			output += this.uid;
		} else if (KeyType.password.equals(this.getKey())) {
			output += this.password;
		} else if (KeyType.name.equals(this.getKey())) {
			output += this.name;
        } else if (KeyType.dob.equals(this.getKey())) {
			output += this.dob;
		} else {
			output += super.getType() + ": " + this.uid + ", " + this.password + ", " + this.name + ", " + this.dob;
		}
		return output;
		
	}

	// Test data initializer
	public static User[] Users() {
		return new User[]{
				new User("100", "Password1234", "Krish", 101906),
				new User("120", "lol1234", "Daniel", 031505)
		};
	}
	
	/* main to test User class
	 * 
	 */
	public static void main(String[] args)
	{
		// Inheritance Hierarchy
		User[] objs = Users();

		// print with title
		User.setOrder(KeyType.title);
		User.print(objs);

		// print name only
		User.setOrder(KeyType.name);
		User.print(objs);
	}

}

User.main(null);
class [LREPL.$JShell$19B$User; 2
Generic: User listed by title
User: 100, Password1234, Krish, 101906
User: 120, lol1234, Daniel, 13125

class [LREPL.$JShell$19B$User; 2
Generic: User listed by name
Krish
Daniel

public class Statistics extends Generics {
	// Class data
	public static KeyTypes key = KeyType.title;  // static initializer
	public static void setOrder(KeyTypes key) { Statistics.key = key; }
	public enum KeyType implements KeyTypes {title, year, overallScore, breakdown}

	// Instance data
	private final int year;
	private final int overallScore;
	private final String breakdown;

	/* constructor
	 *
	 */
	public Statistics(int year, int overallScore, String breakdown)
	{
		super.setType("FRQTypes");
		this.year = year;
		this.overallScore = overallScore;
		this.breakdown = breakdown;
    }

	/* 'Generics' requires getKey to help enforce KeyTypes usage */
	@Override
	protected KeyTypes getKey() { return FRQTypes.key; }
	
	/* 'Generics' requires toString override
	 * toString provides data based off of Static Key setting
	 */
	@Override
	public String toString()
	{
		String output="";
		if (KeyType.year.equals(this.getKey())) {
			output += this.year;
		} else if (KeyType.overallScore.equals(this.getKey())) {
			output += this.overallScore;
		} else if (KeyType.breakdown.equals(this.getKey())) {
			output += this.breakdown;
		}  else {
			output += super.getType() + ": " + this.year + ", " + this.overallScore + ", " + this.breakdown;
		}
		return output;
		
	}

	// Test data initializer
	public static Statistics[] Statistics() {
		return new Statistics[]{
				new Statistics(2021, 14, "for each"),
                // new Statistics()
		};
	}
	
	/* main to test FRQTypes class
	 * 
	 */
	public static void main(String[] args)
	{
		// Inheritance Hierarchy
		Statistics[] objs = Statistics();

		// print with title
		Statistics.setOrder(KeyType.title);
		Statistics.print(objs);

		// print name only
		Statistics.setOrder(KeyType.year);
		Statistics.print(objs);
	}

}
Statistics.main(null);
public class Stats extends Generics {
	// Class data
	public static KeyTypes key = KeyType.title;  // static initializer
	public static void setOrder(KeyTypes key) { Stats.key = key; }
	public enum KeyType implements KeyTypes {title, year, score, problem1, problem2, problem3, problem4}

	// Instance data
	private final int year;
	private final int score;
	private final int problem1;
	private final int problem2;
	private final int problem3;
	private final int problem4;

	/* constructor
	 *
	 */
	public Stats(int year, int score, int problem1, int problem2, int problem3, int problem4)
	{
		super.setType("FRQ");
		this.year = year;
		this.score = score;
		this.problem1 = problem1;
		this.problem2 = problem2;
		this.problem3 = problem3;
		this.problem4 = problem4;
	}

	/* 'Generics' requires getKey to help enforce KeyTypes usage */
	@Override
	protected KeyTypes getKey() { return Stats.key; }
	
	/* 'Generics' requires toString override
	 * toString provides data based off of Static Key setting
	 */
	@Override
	public String toString()
	{
		String output="";
		if (KeyType.year.equals(this.getKey())) {
			output += this.year;
		} else if (KeyType.score.equals(this.getKey())) {
			output += this.score;
		} else if (KeyType.problem1.equals(this.getKey())) {
			output += this.problem1;
		}  else if (KeyType.problem2.equals(this.getKey())) {
			output += this.problem2;
		} else if (KeyType.problem3.equals(this.getKey())) {
			output += this.problem3;
		} else if (KeyType.problem4.equals(this.getKey())) {
			output += this.problem4;
		} else {
			output += super.getType() + ": " + this.year + ", Overall Score: " + this.score + 
			", \n\t\tProblem 1 Points: " + this.problem1 + ", \n\t\tProblem 2 Points: " + this.problem2 + ", \n\t\tProblem 3 Points: " + this.problem3 + ", \n\t\tProblem 4 Points: " + this.problem4;
		}
		return output;
		
	}

	// Test data initializer
	public static Stats[] Stats() {
		return new Stats[]{
				new Stats(2010, 23, 3, 3, 9, 8),
				new Stats(2017, 33, 7, 9, 8, 9), 
				new Stats(2021, 20, 5, 9, 4, 2),
				new Stats(2015, 26, 7, 8, 6, 5),
		};
	}
	
	/* main to test Stats class
	 * 
	 */
	public static void main(String[] args)
	{
		// Inheritance Hierarchy
		Stats[] objs = Stats();

		// print with title
		Stats.setOrder(KeyType.title);
		Stats.print(objs);
	}

}
Stats.main(null);
class [LREPL.$JShell$40S$Stats; 4
Generic: FRQ listed by title
FRQ: 2010, Overall Score: 23, 
		Problem 1 Points: 3, 
		Problem 2 Points: 3, 
		Problem 3 Points: 9, 
		Problem 4 Points: 8
FRQ: 2017, Overall Score: 33, 
		Problem 1 Points: 7, 
		Problem 2 Points: 9, 
		Problem 3 Points: 8, 
		Problem 4 Points: 9
FRQ: 2021, Overall Score: 20, 
		Problem 1 Points: 5, 
		Problem 2 Points: 9, 
		Problem 3 Points: 4, 
		Problem 4 Points: 2
FRQ: 2015, Overall Score: 26, 
		Problem 1 Points: 7, 
		Problem 2 Points: 8, 
		Problem 3 Points: 6, 
		Problem 4 Points: 5