First commit

This commit is contained in:
2025-09-10 14:25:37 -04:00
parent dfbabbd9cd
commit 1fdbcd7fb8
161 changed files with 13820 additions and 2 deletions

BIN
Homework 8/Homework 8.docx Executable file

Binary file not shown.

View File

@@ -0,0 +1,72 @@
package grading;
/**
* Abstract implementation of Grade meant for extension with custom getValue implementations.
*/
public abstract class AbstractGrade implements Grade
{
private String key;
/**
* @param key
* The key for the Grade
* @throws IllegalArgumentException
*/
public AbstractGrade(final String key) throws IllegalArgumentException
{
if (key == null || key.isEmpty())
throw new IllegalArgumentException();
this.key = key;
};
/**
* @param other
* Another Grade object to compare to this grade
* @return The value 0 if other's value is equal to this Grade's value; -1 if this Grade's value
* is less than other's; and 1 if this Grade's value is greater than other's.
*/
public int compareTo(final Grade other)
{
Double value = this.getValue();
Double otherValue = other.getValue();
if (value == null)
{
return (otherValue == null) ? 0 : -1;
}
if (otherValue == null)
{
return 1;
}
return value.compareTo(otherValue);
}
/**
* @return The key of for this Grade
*/
public String getKey()
{
return key;
}
/**
* @return The value for this Grade
*/
public abstract Double getValue();
/**
* @return A string representation of the Grade
*/
public String toString()
{
Double value = this.getValue();
if (value == null)
{
return String.format("%s: NA", key);
}
return String.format("%s: %5.1f", key, value);
}
}

View File

@@ -0,0 +1,111 @@
package grading;
import java.util.ArrayList;
import java.util.List;
/**
* An aggregate implementation of Grade that allows for multiple grades to be treated as one.
*/
public class CompositeGrade extends AbstractGrade
{
private Filter filter;
private GradingStrategy strategy;
private List<Grade> components = new ArrayList<Grade>();
/**
* @param key
* The key for the CompositeGrade
* @throws IllegalArgumentException
*/
public CompositeGrade(final String key) throws IllegalArgumentException
{
super(key);
}
/**
* @param grade
* A sub-grade to add to the CompositeGrade
*/
public void add(final Grade grade)
{
components.add(grade);
}
/**
* @return The sub-grades that make up the CompositeGrade
*/
public List<Grade> getComponents()
{
return components;
}
/**
* @return The Filter that get's applied to the components of the CompositeGrade when calculating
* its value
*/
public Filter getFilter()
{
return filter;
}
/**
* @return The GradingStrategy used on the components of the CompositeGrade to calculate its value
*/
public GradingStrategy getStrategy()
{
return strategy;
}
/**
* @return The calculated value of the CompositeGrade using the Filter and GradingStrategy
* supplied
*/
public Double getValue()
{
if (strategy == null)
return null;
List<Grade> componentsToCalculate = components;
if (filter != null)
{
try
{
componentsToCalculate = filter.apply(components);
}
catch (SizeException e)
{
return null;
}
}
try
{
return strategy.calculate(getKey(), componentsToCalculate).getValue();
}
catch (SizeException e)
{
return null;
}
}
/**
* @param filter
* The filter to be applied to the components of the CompositeGrade when calculating its
* value
*/
public void setFilter(final Filter filter)
{
this.filter = filter;
}
/**
* @param strategy
* The GradingStrategy to be used on the components of the CompositeGrade to calculate
* its value
*/
public void setStrategy(final GradingStrategy strategy)
{
this.strategy = strategy;
}
}

View File

@@ -0,0 +1,79 @@
package grading;
import java.util.*;
/**
* A filter to apply to a list of grades to optionally drop the lowest or highest grades.
*/
public class DropFilter implements Filter
{
private boolean shouldDropLowest;
private boolean shouldDropHighest;
/**
* Default constructor that will drop the lowest and highest grades.
*/
public DropFilter()
{
this.shouldDropLowest = true;
this.shouldDropHighest = true;
}
/**
* @param shouldDropLowest
* Whether or not to drop the lowest grade
* @param shouldDropHighest
* Whether or not to drop the highest grade
*/
public DropFilter(final boolean shouldDropLowest, final boolean shouldDropHighest)
{
this.shouldDropLowest = shouldDropLowest;
this.shouldDropHighest = shouldDropHighest;
}
/**
* @param grades
* The list of grades to apply the DropFilter to.
* @return A new list containing the original grades with filter applied
*/
public List<Grade> apply(final List<Grade> grades) throws SizeException
{
int minSize = (shouldDropLowest ? 1 : 0) + (shouldDropHighest ? 1 : 0);
if (grades == null || grades.size() <= minSize)
{
throw new SizeException();
}
List<Grade> droppedGrades = new ArrayList<Grade>();
droppedGrades.addAll(grades);
Grade lowest = null;
Grade highest = null;
for (Grade grade : grades)
{
if (lowest == null || grade.compareTo(lowest) < 0)
{
lowest = grade;
}
if (highest == null || grade.compareTo(highest) > 0)
{
highest = grade;
}
}
if (shouldDropLowest)
{
droppedGrades.remove(lowest);
}
if (shouldDropHighest)
{
droppedGrades.remove(highest);
}
return droppedGrades;
}
}

View File

@@ -0,0 +1,17 @@
package grading;
import java.util.*;
/**
* An interface for filters that can be applied to a list of grades.
*/
public interface Filter
{
/**
* @param grades
* The list of grades to provide the filter to
* @return A new list contain the original grades with the filter applied
* @throws SizeException
*/
public List<Grade> apply(List<Grade> grades) throws SizeException;
}

View File

@@ -0,0 +1,17 @@
package grading;
/**
* An interface for implementing a grade with a key and a value to store the score.
*/
public interface Grade extends Comparable<Grade>
{
/**
* @return The key of for this Grade
*/
public String getKey();
/**
* @return The value for this Grade
*/
public Double getValue();
}

View File

@@ -0,0 +1,19 @@
package grading;
import java.util.*;
/**
* An interface for strategies of calculating a final grade from a list of grades.
*/
public interface GradingStrategy
{
/**
* @param key
* The key for the final calculated grade
* @param grades
* The list of grades to from which to calculate the final grade
* @return A new grade representing the final grade calculated from grades
* @throws SizeException
*/
public Grade calculate(String key, List<Grade> grades) throws SizeException;
}

View File

@@ -0,0 +1,74 @@
package grading;
/**
* A data class for storing a grade with a key and a value to store the score.
*/
public class LeafGrade extends AbstractGrade
{
private Double value;
/**
* @param key
* The type of grade
*/
public LeafGrade(final String key) throws IllegalArgumentException
{
this(key, 0.0);
}
/**
* @param key
* The type of grade
* @param value
* The score for the grade
*/
public LeafGrade(final String key, final double value) throws IllegalArgumentException
{
this(key, Double.valueOf(value));
}
/**
* @param key
* The type of grade
* @param value
* The score for the grade
*/
public LeafGrade(final String key, final Double value) throws IllegalArgumentException
{
super(key);
this.value = value;
}
/**
* @return The value of the grade
*/
public Double getValue()
{
return value;
}
/**
* @param key
* The key for the Grade
* @param value
* The string representation of the value for the Grade
* @return A new LeafGrade object with the passed key and value
* @throws IllegalArgumentException
*/
public static LeafGrade parseLeafGrade(final String key, final String value)
throws IllegalArgumentException
{
if (value == null)
return new LeafGrade(key, null);
try
{
Double parsedValue = Double.valueOf(value);
return new LeafGrade(key, parsedValue);
}
catch (NumberFormatException e)
{
return new LeafGrade(key, null);
}
}
}

View File

@@ -0,0 +1,35 @@
package grading;
/**
* A utility class for supplying a default double value in the case that a provided double is null.
*/
public class Missing
{
private static double DEFAULT_MISSING_VALUE = 0.0;
private Missing()
{
}
/**
* @param number
* The supplied double to check for null.
* @return Either number if it was not null, or a default value of 0.0 if it was.
*/
public static double doubleValue(final Double number)
{
return number != null ? number : DEFAULT_MISSING_VALUE;
}
/**
* @param number
* The supplied double to check for null.
* @param missingValue
* The default value to return if number was null
* @return Either number if it was not null, or missingValue if it was
*/
public static double doubleValue(final Double number, final double missingValue)
{
return number != null ? number : missingValue;
}
}

View File

@@ -0,0 +1,19 @@
package grading;
/**
* A custom exception for cases where a provided list does not meet a size requirement.
*/
public class SizeException extends RuntimeException
{
/**
* Exception identifier.
*/
static final long serialVersionUID = 1L;
/**
* Creates a new SizeException instance.
*/
public SizeException()
{
}
}

View File

@@ -0,0 +1,40 @@
package grading;
import java.util.List;
/**
* An implementation GradingStrategy that simply totals the scores for all grades.
*/
public class TotalStrategy implements GradingStrategy
{
/**
* Creates a new instance of TotalStrategy.
*/
public TotalStrategy()
{
}
/**
* @param key
* The key of the final totaled grade
* @param grades
* The list of grades to calculate the final total grade from
* @return A new grade representing the total of grades
*/
public Grade calculate(final String key, final List<Grade> grades) throws SizeException
{
if (grades == null || grades.isEmpty())
{
throw new SizeException();
}
Double total = 0.0;
for (Grade grade : grades)
{
total += Missing.doubleValue(grade.getValue());
}
return new LeafGrade(key, total);
}
}

View File

@@ -0,0 +1,122 @@
package grading;
import java.util.*;
/**
* A GradingStrategy that calculates the weighted average of a collection of grades.
*
* @author Ann E. Coda, Sagacious Media
*/
public class WeightedAverageStrategy implements GradingStrategy
{
private static final Double ZERO = 0.0; // FIX: Unnecessary Double constructor removed
private boolean shouldIgnoreMissing;
private Map<String, Double> weights;
/**
* Default Constructor.
*/
public WeightedAverageStrategy()
{
this(null, true);
}
/**
* Explicit Value Constructor.
*
* @param weights
* The Map of weights to use (or null for equal weights)
*/
public WeightedAverageStrategy(final Map<String, Double> weights) // FIX: Parameters changed to be
// final
{
// FIX: Removed super() call since we are not extending anything
// FIX: changed redundant assignments with call to two-parameter constructor
this(weights, true);
}
/**
* Explicit Value Constructor.
*
* @param weights
* The Map of weights to use (or null for equal weights)
* @param shouldIgnoreMissing
* true to ignore missing values; false to treat as 0
*/
public WeightedAverageStrategy(final Map<String, Double> weights,
final boolean shouldIgnoreMissing) // FIX: Parameters changed to be final
{
// FIX: weights changed to be assigned to this.weights
this.weights = weights;
// FIX: shouldIgnoreMissing changed to be assigned to this.shouldIgnoreMissing
this.shouldIgnoreMissing = shouldIgnoreMissing;
}
/**
* Use this GradingStrategy to calculate a numeric grade.
*
* Note: If there are no weights (i.e., the weights Map is null) then each grade is assumed to
* have a weight of 1. If there are weights but an individual Grade does not have a corresponding
* weight its weight is assumed to be 0.0.
*
* @param key
* The key for the Grade to return
* @param grades
* The collection of grades
* @return The numeric grade
*/
public Grade calculate(final String key, final List<Grade> grades) // FIX: Parameters changed to
// be final
{
// Early return
// FIX: Swapped null and size checks so the condition short circuits if grades is null
if (grades == null || grades.size() == 0)
return new LeafGrade(key, ZERO);
double weightedTotal = 0.0; // FIX: Changed name from "numerator" to the more meaningful
// "weightedSum"
double weightSum = 0.0; // FIX: Changed name from "denominator" to the more meaningful
// "weightSum"
// FIX: Changed indexing variable name from ambiguous "g" to "grade"
for (Grade grade : grades)
{
// FIX: Changed "grade" to "gradeValue"
Double gradeValue = grade.getValue();
// FIX: Changed "weight" to "gradeWeight" and replaced "w"
Double gradeWeight = 1.0; // Assigning the default null-weight value to remove else branch
// Handle a missing Grade
// FIX: Joined nested if statements into one and removed incorrect comment
// FIX: Removed incorrect calculation, missing values are not skipped before looking checking
// for a weight
if (gradeValue == null && shouldIgnoreMissing)
continue;
// FIX: Inverted condition to remove else branch
if (weights != null) // All weights are unspecified
{
// FIX: Added Missing.doubleValue to handle null weights using 0.0
gradeWeight = Missing.doubleValue(weights.get(grade.getKey()));
}
// Handle an existing Grade
// FIX: Use a default value of 0.0 if gradeValue is null
weightedTotal += gradeWeight * Missing.doubleValue(gradeValue);
// FIX: Weight sum now incremented by grade weight, not just 1.0
weightSum += gradeWeight;
}
// Handle calculating weighted average
// FIX: Default value of zero will be used for the weighted average if the weightSum is zero.
Double weightedAverage = ZERO;
if (weightSum > ZERO)
{
weightedAverage = weightedTotal / weightSum;
}
// FIX: Unnecessary Double constructor removed
return new LeafGrade(key, weightedAverage);
}
}

View File

@@ -0,0 +1,61 @@
package grading;
import java.util.*;
/**
* An implementation GradingStrategy that applies weights to specific grade keys and weighs each
* grade before summing a total.
*/
public class WeightedTotalStrategy implements GradingStrategy
{
private Map<String, Double> weights;
/**
* Creates a new instance WeightedTotalStrategy.
*/
public WeightedTotalStrategy()
{
}
/**
* Creates a new instance WeightedTotalStrategy.
*
* @param weights
* A map of keys and weights to be applied to the grades during calculation.
*/
public WeightedTotalStrategy(final Map<String, Double> weights)
{
this.weights = weights;
}
/**
* @param key
* The key of the final totaled grade
* @param grades
* The list of grades to calculate the final total grade from
* @return A new grade representing the total of the weighted grades
*/
public Grade calculate(final String key, final List<Grade> grades) throws SizeException
{
if (grades == null || grades.isEmpty())
{
throw new SizeException();
}
Double total = 0.0;
for (Grade grade : grades)
{
Double weight = 1.0;
if (weights != null && weights.containsKey(grade.getKey()))
{
Double mapWeight = weights.get(grade.getKey());
weight = Math.max(mapWeight, 0.0);
}
total += Missing.doubleValue(grade.getValue()) * weight;
}
return new LeafGrade(key, total);
}
}

View File

@@ -0,0 +1,70 @@
package grading.io;
import grading.*;
import java.util.*;
/**
* An aggregate for Grade representing a specific type of assignment with a specific Filter and
* GradingStrategy.
*/
public class Category
{
private final String key;
private final List<Grade> grades;
private final Filter filter;
private final GradingStrategy strategy;
/**
* @param key
* The key for the Category
* @param grades
* The Grades in the category
* @param filter
* The filter to apply to the collection of grades
* @param strategy
* The strategy used to calculate the final Category Grade
*/
public Category(final String key, final List<Grade> grades, final Filter filter,
final GradingStrategy strategy)
{
if (key == null || key.isEmpty())
throw new IllegalArgumentException();
this.key = key;
this.grades = grades;
this.filter = filter;
this.strategy = strategy;
}
/**
* @return The filter for the Category
*/
public Filter getFilter()
{
return filter;
}
/**
* @return The GradingStrategy for the Category
*/
public GradingStrategy getStrategy()
{
return strategy;
}
/**
* @return The Grades making up the Category
*/
public List<Grade> getGrades()
{
return grades;
}
/**
* @return The key for the Category
*/
public String getKey()
{
return key;
}
}

View File

@@ -0,0 +1,41 @@
package grading.io;
import grading.GradingStrategy;
import java.util.List;
/**
* An aggregate for Categories with a specific GradingStrategy for calculating the final Course
* Grade.
*/
public class Course
{
private final GradingStrategy strategy;
private final List<Category> categories;
/**
* @param categories The Categories making up the Course
* @param strategy The strategy by which to calculate the final grade
*/
public Course(final List<Category> categories, final GradingStrategy strategy)
{
this.categories = categories;
this.strategy = strategy;
}
/**
* @return The Categories making up the Course
*/
public List<Category> getCategories()
{
return categories;
}
/**
* @return The strategy used for calculating the final Grade for the Course
*/
public GradingStrategy getStrategy()
{
return strategy;
}
}

View File

@@ -0,0 +1,103 @@
package grading.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import grading.*;
/**
* A utility class for reading in Grade data from a .grd file and constructing Categories and
* Courses.
*/
public class CourseReader
{
private static final String TAB_DELIMITER = "\t";
/**
* Empty constructor so the coverage tests stop yelling at me.
*/
public CourseReader()
{
}
/**
* @param in
* A BufferedReader for reading data from the file
* @param size
* The number of grade entries in a given category to read
* @param key
* The key for the category
* @param filter
* The filter to construct the Category with
* @return A Category containing all the grades read from the file
* @throws IOException
* @throws IllegalArgumentException
*/
public static CompositeGrade readCategory(final BufferedReader in, final int size,
final String key, final Filter filter) throws IOException, IllegalArgumentException
{
final List<Grade> grades = new ArrayList<Grade>();
for (int i = 0; i < size; i++)
{
final String[] gradeData = in.readLine().split(TAB_DELIMITER);
String gradeKey = gradeData[0];
String value = gradeData.length > 1 ? gradeData[1] : null;
grades.add(LeafGrade.parseLeafGrade(gradeKey, value));
}
CompositeGrade compositeGrade = new CompositeGrade(key);
compositeGrade.setFilter(filter);
compositeGrade.setStrategy(new TotalStrategy());
grades.forEach((grade) -> compositeGrade.add(grade));
return compositeGrade;
}
/**
* @param in
* A BufferedReader for reading data from the file
* @param size
* The number of categories in the file to read
* @return A Course consisting of all Categories read from the file
* @throws IOException
*/
public static CompositeGrade readCourse(final BufferedReader in, final int size)
throws IOException
{
final List<CompositeGrade> categories = new ArrayList<CompositeGrade>();
final Map<String, Double> weights = new HashMap<String, Double>();
for (int i = 0; i < size; i++)
{
final String[] categoryFields = in.readLine().split(TAB_DELIMITER);
final String categoryName = categoryFields[0];
final int gradeCount = Integer.parseInt(categoryFields[1]);
weights.put(categoryName, Double.parseDouble(categoryFields[2]));
final boolean shouldDropLowest = Boolean.parseBoolean(categoryFields[3]);
final boolean shouldDropHighest = Boolean.parseBoolean(categoryFields[4]);
final Filter filter = new DropFilter(shouldDropLowest, shouldDropHighest);
final CompositeGrade categoryGrade = CourseReader.readCategory(in, gradeCount, categoryName,
filter);
categories.add(categoryGrade);
}
final GradingStrategy strategy = new WeightedTotalStrategy(weights);
CompositeGrade courseGrade = new CompositeGrade("Course");
courseGrade.setStrategy(strategy);
categories.forEach((grade) -> courseGrade.add(grade));
return courseGrade;
}
}

View File

@@ -0,0 +1,72 @@
package gui;
import java.awt.FlowLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import grading.CompositeGrade;
import grading.Grade;
import grading.LeafGrade;
/**
* A grouping of LeafGradeFields for a specific category of grades.
*/
public class CategoryField extends JPanel
{
private static final long serialVersionUID = 1L;
private List<LeafGradeField> fields;
private CompositeGrade template;
/**
* @param template The CompositeGrade to use as a default key and value.
*/
public CategoryField(final CompositeGrade template)
{
this.template = template;
this.fields = new ArrayList<LeafGradeField>();
setLayout(new FlowLayout(FlowLayout.LEFT));
setBorder(BorderFactory.createTitledBorder(template.getKey()));
for (Grade grade : template.getComponents())
{
if (grade instanceof LeafGrade)
{
LeafGradeField field = new LeafGradeField((LeafGrade) grade);
fields.add(field);
add(field);
}
}
}
/**
* @return A new Grade object representing the composite grade of all fields in the category.
*/
public Grade getGrade()
{
CompositeGrade grade = new CompositeGrade(getKey());
grade.setFilter(template.getFilter());
grade.setStrategy(template.getStrategy());
fields.forEach((field) -> grade.add(field.getGrade()));
return grade;
}
/**
* @return The key for this category
*/
public String getKey()
{
return template.getKey();
}
/**
* Clears any text from all grades in this category.
*/
public void reset()
{
fields.forEach((field) -> field.reset());
}
}

View File

@@ -0,0 +1,105 @@
package gui;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import grading.CompositeGrade;
import grading.Grade;
/**
* The main window for the Gradient Application.
*/
public class GradientWindow extends JFrame
{
private static final long serialVersionUID = 1L;
private List<CategoryField> categoryFields;
private CompositeGrade template;
/**
* @param template
* The CompositeGrade to use as a default key and value for the total calculated grade.
*/
public GradientWindow(final CompositeGrade template)
{
this.template = template;
this.categoryFields = new ArrayList<CategoryField>();
setSize(400, 400);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Gradient by tamassno");
Container contentPane = getContentPane();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
for (Grade grade : template.getComponents())
{
if (grade instanceof CompositeGrade)
{
CategoryField category = new CategoryField((CompositeGrade) grade);
contentPane.add(category);
categoryFields.add(category);
}
}
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenuItem exitItem = new JMenuItem("Exit");
exitItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(final ActionEvent e)
{
dispose();
}
});
fileMenu.add(exitItem);
JMenu editMenu = new JMenu("Edit");
JMenuItem calculateItem = new JMenuItem("Calculate");
calculateItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(final ActionEvent e)
{
JOptionPane.showMessageDialog(null, String.valueOf(calculateGrade().getValue()),
"Course Grade", JOptionPane.INFORMATION_MESSAGE);
}
});
editMenu.add(calculateItem);
JMenuItem resetItem = new JMenuItem("Reset");
resetItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(final ActionEvent e)
{
categoryFields.forEach((field) -> field.reset());
}
});
editMenu.add(resetItem);
menuBar.add(fileMenu);
menuBar.add(editMenu);
setJMenuBar(menuBar);
}
private Grade calculateGrade()
{
CompositeGrade grade = new CompositeGrade(template.getKey());
grade.setFilter(template.getFilter());
grade.setStrategy(template.getStrategy());
categoryFields.forEach((field) -> grade.add(field.getGrade()));
return grade;
}
}

View File

@@ -0,0 +1,92 @@
package gui;
import java.awt.Dimension;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.BorderFactory;
import javax.swing.JTextField;
import grading.Grade;
import grading.LeafGrade;
/**
* A text field for entering grades.
*/
public class LeafGradeField extends JTextField implements FocusListener
{
private static final long serialVersionUID = 1L;
private LeafGrade template;
/**
* @param template
* The LeafGrade to use as a default key and value.
*/
public LeafGradeField(final LeafGrade template)
{
this.template = template;
addFocusListener(this);
setBorder(BorderFactory.createTitledBorder(template.getKey()));
setColumns(template.getKey().length() + 1);
Double gradeValue = template.getValue();
if (gradeValue != null)
{
setText(String.valueOf(gradeValue));
}
}
/**
* @return A new Grade object with the value entered in the text field.
*/
public Grade getGrade()
{
Double value;
try
{
value = Double.valueOf(getText());
}
catch (NumberFormatException e)
{
value = null;
}
return new LeafGrade(getKey(), value);
}
/**
* @return The key for this text field
*/
public String getKey()
{
return template.getKey();
}
/**
* Clears any text from the text field.
*/
public void reset()
{
setText("");
}
@Override
public Dimension getMaximumSize()
{
return getMinimumSize();
}
@Override
public void focusGained(final FocusEvent e)
{
selectAll();
}
@Override
public void focusLost(final FocusEvent e)
{
setCaretPosition(getDocument().getLength());
setSelectionStart(getSelectionEnd());
}
}

View File

@@ -0,0 +1,51 @@
package testing;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import static org.junit.jupiter.api.Assertions.*;
import grading.*;
import grading.io.Category;
import java.util.*;
class CategoryTest
{
private Filter filter;
private GradingStrategy strategy;
private List<Grade> grades;
private String validKey;
@BeforeEach
void setUp()
{
filter = new DropFilter();
strategy = new TotalStrategy();
grades = new ArrayList<Grade>();
validKey = "math";
}
@Test
void badKeyTest()
{
assertThrows(IllegalArgumentException.class,
() -> new Category(null, grades, filter, strategy));
assertThrows(IllegalArgumentException.class, () -> new Category("", grades, filter, strategy));
}
@Test
void getterTest()
{
Grade grade = new LeafGrade("HW1", 95.0);
grades.add(grade);
Category category = new Category(validKey, grades, filter, strategy);
assertEquals(validKey, category.getKey());
assertSame(grades, category.getGrades());
assertEquals(filter, category.getFilter());
assertEquals(strategy, category.getStrategy());
}
}

View File

@@ -0,0 +1,126 @@
package testing;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import grading.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
class CompositeGradeTest
{
private static final String COMPOSITE_KEY = "Composite";
private CompositeGrade compositeGrade;
private GradingStrategy mockStrategy;
private Filter mockFilter;
private List<Grade> mockGrades;
@BeforeEach
void setUp()
{
compositeGrade = new CompositeGrade(COMPOSITE_KEY);
mockStrategy = new TotalStrategy();
mockFilter = new DropFilter(true, true);
mockGrades = new ArrayList<Grade>();
mockGrades.add(new LeafGrade("HW1", 75.0));
mockGrades.add(new LeafGrade("HW2", 50.0));
mockGrades.add(new LeafGrade("HW3", 25.0));
mockGrades.add(new LeafGrade("HW4", 100.0));
}
@Test
void constructor()
{
// Ensure constructor properly initializes the object
assertEquals(compositeGrade.getKey(), COMPOSITE_KEY);
assertEquals(compositeGrade.getValue(), null);
}
@Test
void badConstructor()
{
assertThrows(IllegalArgumentException.class, () -> new CompositeGrade(null));
assertThrows(IllegalArgumentException.class, () -> new CompositeGrade(""));
}
@Test
void addAndRetrieveComponents()
{
// Test that adding a grade adds it to the components list
mockGrades.forEach((grade) -> compositeGrade.add(grade));
assertEquals(mockGrades, compositeGrade.getComponents());
}
@Test
void setAndGetFilter()
{
// Test setting and getting the filter
compositeGrade.setFilter(mockFilter);
assertEquals(mockFilter, compositeGrade.getFilter());
}
@Test
void setAndGetStrategy()
{
// Test setting and getting the grading strategy
compositeGrade.setStrategy(mockStrategy);
assertEquals(mockStrategy, compositeGrade.getStrategy());
}
@Test
void getValueNullIfNoStrategy()
{
// Test that getValue returns null when no strategy is assigned
compositeGrade.setStrategy(null);
assertEquals(null, compositeGrade.getValue());
}
@Test
void getValueNullIfFilterThrowsSizeException()
{
// Ensure getValue handles exceptions thrown by the filter
compositeGrade.setFilter(mockFilter);
compositeGrade.setStrategy(mockStrategy);
compositeGrade.add(mockGrades.getFirst());
assertEquals(null, compositeGrade.getValue());
}
@Test
void getValueNullIfStrategyThrowsSizeException()
{
// Ensure getValue handles exceptions thrown by the strategy
compositeGrade.setStrategy(mockStrategy);
assertEquals(null, compositeGrade.getValue());
}
@Test
void getValueFilterIsNull()
{
// Ensure strategy calculates the value when no filter is set
compositeGrade.setFilter(null);
compositeGrade.setStrategy(mockStrategy);
mockGrades.forEach((grade) -> compositeGrade.add(grade));
assertEquals(250.0, compositeGrade.getValue());
}
@Test
void getValueFilterApplies()
{
// Ensure filtered list is passed to strategy for calculation
compositeGrade.setFilter(mockFilter);
compositeGrade.setStrategy(mockStrategy);
mockGrades.forEach((grade) -> compositeGrade.add(grade));
assertEquals(125.0, compositeGrade.getValue());
}
}

View File

@@ -0,0 +1,54 @@
package testing;
import org.junit.jupiter.api.Test;
import grading.*;
import grading.io.*;
import static org.junit.jupiter.api.Assertions.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
class CourseReaderTest
{
@Test
void grdFileTests() throws IOException, SizeException
{
assertNotNull(new CourseReader()); // Nonsense test for coverage
final Double complete01Grade = calculateCourseGradeFromFile("complete_01.grd");
final Double complete02Grade = calculateCourseGradeFromFile("complete_02.grd");
final Double complete03Grade = calculateCourseGradeFromFile("complete_03.grd");
final Double fiveCategories01Grade = calculateCourseGradeFromFile("five-categories_01.grd");
final Double missingAllGrade = calculateCourseGradeFromFile("missing_all.grd");
final Double missingMultipleInEachGrade = calculateCourseGradeFromFile(
"missing_multiple-in-each.grd");
final Double missingOneInEachGrade = calculateCourseGradeFromFile("missing_one-in-each.grd");
final Double missingOneInOneGrade = calculateCourseGradeFromFile("missing_one-in-one.grd");
final Double sixCategories01Grade = calculateCourseGradeFromFile("six-categories_01.grd");
assertEquals(80.7, complete01Grade);
assertEquals(100, complete02Grade);
assertEquals(54.0, complete03Grade);
assertEquals(64.75, fiveCategories01Grade);
assertEquals(0.0, missingAllGrade);
assertEquals(19.5, missingMultipleInEachGrade);
assertEquals(35.2, missingOneInEachGrade);
assertEquals(80.7, missingOneInOneGrade);
assertEquals(76.6, sixCategories01Grade);
}
private static Double calculateCourseGradeFromFile(final String filename)
throws IOException, SizeException
{
BufferedReader in = new BufferedReader(new FileReader(filename));
String line = in.readLine();
int categories = Integer.parseInt(line);
CompositeGrade course = CourseReader.readCourse(in, categories);
in.close();
return course.getValue();
}
}

View File

@@ -0,0 +1,37 @@
package testing;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import grading.*;
import grading.io.Category;
import grading.io.Course;
import java.util.ArrayList;
import java.util.List;
class CourseTest
{
@Test
void testCourseInitialization()
{
// Arrange
final GradingStrategy strategy = new TotalStrategy();
final Filter filter = new DropFilter();
final List<Category> categories = new ArrayList<Category>();
final List<Grade> homeworkGrades = new ArrayList<Grade>();
homeworkGrades.add(new LeafGrade("HW1", 20.0));
homeworkGrades.add(new LeafGrade("HW2", 5.0));
final Category hws = new Category("Homework Assignments", homeworkGrades, filter, strategy);
categories.add(hws);
final Course course = new Course(categories, strategy);
assertEquals(categories, course.getCategories(),
"Categories should match the initialized values");
assertEquals(strategy, course.getStrategy(), "Strategy should match the initialized value");
}
}

View File

@@ -0,0 +1,39 @@
package testing;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
import grading.Grade;
import grading.LeafGrade;
import grading.SizeException;
import grading.DropFilter;
class DropFilterTest
{
@Test
void applyTest()
{
final Grade grade1 = new LeafGrade("HW", 40.0);
final Grade grade2 = new LeafGrade("Midterm", (Double) null);
final Grade grade3 = new LeafGrade("Final", 100.0);
List<Grade> grades = List.of(grade1, grade2, grade3);
DropFilter defaultFilter = new DropFilter();
DropFilter filter1 = new DropFilter(false, false);
DropFilter filter2 = new DropFilter(false, true);
DropFilter filter3 = new DropFilter(true, false);
DropFilter filter4 = new DropFilter(true, true);
assertThrows(SizeException.class, () -> defaultFilter.apply((List<Grade>) null));
assertThrows(SizeException.class, () -> defaultFilter.apply(new ArrayList<Grade>()));
assertIterableEquals(grades, filter1.apply(grades));
assertIterableEquals(List.of(grade1, grade2), filter2.apply(grades));
assertIterableEquals(List.of(grade1, grade3), filter3.apply(grades));
assertIterableEquals(List.of(grade1), filter4.apply(grades));
}
}

View File

@@ -0,0 +1,99 @@
package testing;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import grading.LeafGrade;
class GradeTest
{
private static final String HOMEWORK_KEY = "HW";
private static final String MIDTERM_KEY = "Midterm";
private static final String FINAL_KEY = "Final";
@Test
void defaultValueConstructorTest()
{
LeafGrade grade = new LeafGrade(FINAL_KEY);
assertEquals(FINAL_KEY, grade.getKey());
assertEquals(0.0, grade.getValue());
}
@Test
void givenValueConstructorTest()
{
LeafGrade grade = new LeafGrade(MIDTERM_KEY, 85.5);
assertEquals(MIDTERM_KEY, grade.getKey());
assertEquals(85.5, grade.getValue());
}
@Test
void nullValueConstructorTest()
{
assertThrows(IllegalArgumentException.class, () -> new LeafGrade(null));
assertThrows(IllegalArgumentException.class, () -> new LeafGrade(null, 90.0));
assertThrows(IllegalArgumentException.class, () -> new LeafGrade(null, (Double) null));
}
@Test
void emptyKeyConstructorTest()
{
assertThrows(IllegalArgumentException.class, () -> new LeafGrade(""));
assertThrows(IllegalArgumentException.class, () -> new LeafGrade("", 90.0));
assertThrows(IllegalArgumentException.class, () -> new LeafGrade("", (Double) null));
}
@Test
void nullCompareToTest()
{
LeafGrade grade1 = new LeafGrade(HOMEWORK_KEY, (Double) null);
LeafGrade grade2 = new LeafGrade(HOMEWORK_KEY, 80.0);
LeafGrade grade3 = new LeafGrade(HOMEWORK_KEY, (Double) null);
assertTrue(grade1.compareTo(grade2) < 0);
assertEquals(0, grade1.compareTo(grade3));
assertTrue(grade2.compareTo(grade1) > 0);
}
@Test
void compareToTest()
{
LeafGrade grade1 = new LeafGrade(HOMEWORK_KEY, 75.0);
LeafGrade grade2 = new LeafGrade(HOMEWORK_KEY, 85.0);
LeafGrade grade3 = new LeafGrade(HOMEWORK_KEY, 75.0);
assertTrue(grade1.compareTo(grade2) < 0);
assertEquals(0, grade1.compareTo(grade3));
assertTrue(grade2.compareTo(grade1) > 0);
}
@Test
void toStringTest()
{
LeafGrade grade1 = new LeafGrade(FINAL_KEY, 90.0);
LeafGrade grade2 = new LeafGrade(MIDTERM_KEY, (Double) null);
assertEquals("Final: 90.0", grade1.toString());
assertEquals("Midterm: NA", grade2.toString());
}
@Test
void parseLeafGradeTest()
{
LeafGrade grade1 = LeafGrade.parseLeafGrade(FINAL_KEY, "66.0");
assertEquals(FINAL_KEY, grade1.getKey());
assertEquals(66.0, grade1.getValue());
LeafGrade grade2 = LeafGrade.parseLeafGrade(MIDTERM_KEY, (String) null);
assertEquals(MIDTERM_KEY, grade2.getKey());
assertEquals(null, grade2.getValue());
LeafGrade grade3 = LeafGrade.parseLeafGrade(HOMEWORK_KEY, "INVALID_DOUBLE");
assertEquals(HOMEWORK_KEY, grade3.getKey());
assertEquals(null, grade3.getValue());
assertThrows(IllegalArgumentException.class,
() -> LeafGrade.parseLeafGrade((String) null, "71.0"));
assertThrows(IllegalArgumentException.class, () -> LeafGrade.parseLeafGrade("", "31.0"));
}
}

View File

@@ -0,0 +1,23 @@
package testing;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import grading.Missing;
class MissingTest
{
@Test
void defaultDoubleValueTest()
{
assertEquals(40.0, Missing.doubleValue(40.0));
assertEquals(0.0, Missing.doubleValue((Double) null));
}
@Test
void givenMissingDoubleValueTest()
{
assertEquals(40.0, Missing.doubleValue(40.0, 20.0));
assertEquals(20.0, Missing.doubleValue((Double) null, 20.0));
}
}

View File

@@ -0,0 +1,35 @@
package testing;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
import grading.Grade;
import grading.LeafGrade;
import grading.TotalStrategy;
import grading.SizeException;
class TotalStrategyTest
{
private static final String TOTAL_KEY = "Total";
@Test
void calculateTest()
{
List<Grade> grades = new ArrayList<Grade>();
grades.add(new LeafGrade("HW1", 40.0));
grades.add(new LeafGrade("HW2", (Double) null));
grades.add(new LeafGrade("HW3", 100.0));
TotalStrategy strategy = new TotalStrategy();
assertThrows(SizeException.class, () -> strategy.calculate(TOTAL_KEY, (List<Grade>) null));
assertThrows(SizeException.class, () -> strategy.calculate(TOTAL_KEY, new ArrayList<Grade>()));
Grade total = strategy.calculate(TOTAL_KEY, grades);
assertEquals(TOTAL_KEY, total.getKey());
assertEquals(140.0, total.getValue());
}
}

View File

@@ -0,0 +1,169 @@
package testing;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import grading.Grade;
import grading.LeafGrade;
import grading.WeightedAverageStrategy;
class WeightedAverageStrategyTest
{
private static final String EMPTY_KEY = "EMPTY";
private static final String ZERO_KEY = "0";
private static final String TWO_KEY = "2";
private static final String FIVE_KEY = "5";
private static final String EIGHT_KEY = "8";
private static List<Grade> simpleGrades;
private static List<Grade> withMissingGrades;
private static List<Grade> allZeroGrades;
private static Map<String, Double> simpleWeights;
private static Map<String, Double> withMissingWeights;
private static Map<String, Double> allZeroWeights;
@BeforeAll
static void testCases()
{
Grade gradeMissing = new LeafGrade(EMPTY_KEY, null);
Grade grade0 = new LeafGrade(ZERO_KEY, 0.0);
Grade grade2 = new LeafGrade(TWO_KEY, 2.0);
Grade grade5 = new LeafGrade(FIVE_KEY, 5.0);
Grade grade8 = new LeafGrade(EIGHT_KEY, 8.0);
simpleGrades = new ArrayList<Grade>();
simpleGrades.add(grade2);
simpleGrades.add(grade5);
simpleGrades.add(grade8);
withMissingGrades = new ArrayList<Grade>();
withMissingGrades.add(gradeMissing);
withMissingGrades.add(grade0);
withMissingGrades.add(grade2);
withMissingGrades.add(grade5);
withMissingGrades.add(grade8);
allZeroGrades = new ArrayList<Grade>();
allZeroGrades.add(gradeMissing);
allZeroGrades.add(grade0);
simpleWeights = new HashMap<String, Double>();
simpleWeights.put(EMPTY_KEY, 25.0);
simpleWeights.put(ZERO_KEY, 35.0);
simpleWeights.put(TWO_KEY, 15.0);
simpleWeights.put(FIVE_KEY, 5.0);
simpleWeights.put(EIGHT_KEY, 20.0);
withMissingWeights = new HashMap<String, Double>();
withMissingWeights.put(ZERO_KEY, 35.0);
withMissingWeights.put(TWO_KEY, 15.0);
withMissingWeights.put(EIGHT_KEY, 20.0);
allZeroWeights = new HashMap<String, Double>();
allZeroWeights.put(EMPTY_KEY, null);
allZeroWeights.put(ZERO_KEY, 0.0);
allZeroWeights.put(TWO_KEY, 0.0);
allZeroWeights.put(FIVE_KEY, null);
allZeroWeights.put(EIGHT_KEY, 0.0);
}
Double calcValue(final List<Grade> grades, final WeightedAverageStrategy strategy)
{
return strategy.calculate("test", grades).getValue();
}
@Test
void nullAndEmptyGradesListTest()
{
WeightedAverageStrategy strategy = new WeightedAverageStrategy();
assertEquals(0.0, calcValue(null, strategy));
assertEquals(0.0, calcValue(new ArrayList<Grade>(), strategy));
}
@Test
void nullWeightsTest()
{
WeightedAverageStrategy strategy = new WeightedAverageStrategy();
assertEquals(5.0, calcValue(simpleGrades, strategy));
assertEquals(3.75, calcValue(withMissingGrades, strategy));
assertEquals(0.0, calcValue(allZeroGrades, strategy));
}
@Test
void allZeroWeightsTest()
{
WeightedAverageStrategy shouldIgnoreStrategy = new WeightedAverageStrategy(allZeroWeights);
assertEquals(0.0, calcValue(simpleGrades, shouldIgnoreStrategy));
assertEquals(0.0, calcValue(withMissingGrades, shouldIgnoreStrategy));
assertEquals(0.0, calcValue(allZeroGrades, shouldIgnoreStrategy));
WeightedAverageStrategy shouldNotIgnoreStrategy = new WeightedAverageStrategy(allZeroWeights,
false);
assertEquals(0.0, calcValue(simpleGrades, shouldNotIgnoreStrategy));
assertEquals(0.0, calcValue(withMissingGrades, shouldNotIgnoreStrategy));
assertEquals(0.0, calcValue(allZeroGrades, shouldNotIgnoreStrategy));
}
@Test
void oneParameterConstructorTestSimpleWeights()
{
WeightedAverageStrategy simpleStrategy = new WeightedAverageStrategy(simpleWeights);
assertEquals(5.375, calcValue(simpleGrades, simpleStrategy));
assertEquals(43.0 / 15.0, calcValue(withMissingGrades, simpleStrategy));
assertEquals(0.0, calcValue(allZeroGrades, simpleStrategy));
}
@Test
void oneParameterConstructorTestWithMissingWeights()
{
WeightedAverageStrategy withMissingStrategy = new WeightedAverageStrategy(withMissingWeights);
assertEquals(38.0 / 7.0, calcValue(simpleGrades, withMissingStrategy));
assertEquals(19.0 / 7.0, calcValue(withMissingGrades, withMissingStrategy));
assertEquals(0.0, calcValue(allZeroGrades, withMissingStrategy));
}
@Test
void shouldNotIgnoreMissingNullWeights()
{
WeightedAverageStrategy nullStrategy = new WeightedAverageStrategy(null, false);
assertEquals(5.0, calcValue(simpleGrades, nullStrategy));
assertEquals(3.0, calcValue(withMissingGrades, nullStrategy));
assertEquals(0.0, calcValue(allZeroGrades, nullStrategy));
}
@Test
void shouldNotIgnoreMissingSimpleWeights()
{
WeightedAverageStrategy simpleStrategy = new WeightedAverageStrategy(simpleWeights, false);
assertEquals(5.375, calcValue(simpleGrades, simpleStrategy));
assertEquals(2.15, calcValue(withMissingGrades, simpleStrategy));
assertEquals(0.0, calcValue(allZeroGrades, simpleStrategy));
}
@Test
void shouldNotIgnoreMissingWithMissingWeights()
{
WeightedAverageStrategy withMissingStrategy = new WeightedAverageStrategy(withMissingWeights,
false);
assertEquals(38.0 / 7.0, calcValue(simpleGrades, withMissingStrategy));
assertEquals(19.0 / 7.0, calcValue(withMissingGrades, withMissingStrategy));
assertEquals(0.0, calcValue(allZeroGrades, withMissingStrategy));
}
}

View File

@@ -0,0 +1,51 @@
package testing;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
import grading.Grade;
import grading.LeafGrade;
import grading.WeightedTotalStrategy;
import grading.SizeException;
class WeightedTotalStrategyTest
{
private static final String HOMEWORK_KEY = "HW";
private static final String MIDTERM_KEY = "Midterm";
private static final String FINAL_KEY = "Final";
private static final String TOTAL_KEY = "Total";
@Test
void calculateTest()
{
List<Grade> grades = new ArrayList<Grade>();
grades.add(new LeafGrade(HOMEWORK_KEY, 40.0));
grades.add(new LeafGrade(MIDTERM_KEY, (Double) null));
grades.add(new LeafGrade(FINAL_KEY, 100.0));
Map<String, Double> weights = new HashMap<String, Double>();
weights.put(MIDTERM_KEY, 2.0);
weights.put(FINAL_KEY, 3.0);
WeightedTotalStrategy defaultStrategy = new WeightedTotalStrategy();
assertThrows(SizeException.class,
() -> defaultStrategy.calculate(TOTAL_KEY, (List<Grade>) null));
assertThrows(SizeException.class,
() -> defaultStrategy.calculate(TOTAL_KEY, new ArrayList<Grade>()));
Grade total = defaultStrategy.calculate(TOTAL_KEY, grades);
assertEquals(TOTAL_KEY, total.getKey());
assertEquals(140.0, total.getValue());
WeightedTotalStrategy strategy = new WeightedTotalStrategy(weights);
Grade weightedTotal = strategy.calculate(TOTAL_KEY, grades);
assertEquals(TOTAL_KEY, weightedTotal.getKey());
assertEquals(340.0, weightedTotal.getValue());
}
}