/**
* This class encapsulates a single cell within a spreadsheet.
*/
public class Cell {
private SpreadSheet spreadsheet; // spreadsheet to which cell belongs,
// necessary for resolving cell
// references
private String expression; // expression within cell in reverse Polish
// notation
private double value; // numerical value of evaluating expression
private boolean valueValid; // whether a valid value has been found so
// far. This will change from false to true
// once all cell references have been
// resolved, provided that there are no
// circular references
/**
* Constructor for a cell belonging to a particular spreadsheet.
*
* @param spreadsheet
* SpreadSheet to which Cell belongs.
* @param expression
* expression within cell.
*/
public Cell(SpreadSheet spreadsheet, String expression) {
this.spreadsheet = spreadsheet;
this.expression = expression;
}
/**
* Evaluates expression within a cell. Expression must be in reverse Polish
* notation.
*
* @throws InvalidOperatorException
* if cell expression contains an invalid operator.
* @throws InvalidValueException
* if cell expression contains a reference to a cell that does
* not currently have a valid value.
*/
public void evaluate() throws InvalidOperatorException {
if (!valueValid) { // prevent reevaluation of cells that have valid
// values
try {
// create stack containing termterms in expression
Deque<String> expressionStack = new ArrayDeque<String>(
Arrays.asList(expression.split("\\s")));
value = evaluateRpn(expressionStack);
valueValid = true;
} catch (UnresolvedReferenceException e) {
// no action is necessary if a reference is unresolved, since it
// may be resolved at a later iteration during the solution of
// the spreadsheet
}
}
}
/**
* Get value of a cell. This is the numerical value resulting from
* evaluating the cell's expression.
*
* @return numerical value resulting from evaluating cell's expression.
* @throws InvalidValueException
* if a valid value is not currently available.
*/
public double getValue() throws InvalidValueException {
if (isValueValid()) {
return value;
} else {
throw new InvalidValueException();
}
}
/**
* Check if a valid numerical value has been evaluated for the cell. This
* will be true when evaluate() is called on the cell and all of the cell
* references in the cell's expression can be resolved.
*
* @return whether cell value is valid.
*/
public boolean isValueValid() {
return valueValid;
}
/**
* Evaluate an expression contained within an ExpressionStack.
*
* @param expressionStack
* an expression represented as a stack of individual terms.
* @return evaluation of expression
* @throws InvalidOperatorException
* @throws UnresolvedReferenceException
* @throws InvalidValueException
*/
private double evaluateRpn(Deque<String> expressionStack)
throws InvalidOperatorException, UnresolvedReferenceException {
String term = expressionStack.removeLast();
if (isCellReference(term)) {
// if last term in expression is a cell reference then resolve it
return resolveCellReference(term);
} else {
double x, y;
try {
// if last term in expression is double then return it
x = Double.parseDouble(term);
} catch (NumberFormatException e) {
// otherwise last term is an operator, evaluate operands and
// apply operator
y = evaluateRpn(expressionStack);
x = evaluateRpn(expressionStack);
x = applyOperator(x, y, term);
}
return x;
}
}
/**
* Apply operator to operands x and y.
*
* @param x
* first operand.
* @param y
* second operand.
* @param operator
* @return result of operation
* @throws InvalidOperatorException
*/
private double applyOperator(double x, double y, String operator)
throws InvalidOperatorException {
if (operator.equals("+"))
return x + y;
else if (operator.equals("-"))
return x - y;
else if (operator.equals("*"))
return x *= y;
else if (operator.equals("/"))
return x / y;
else
throw new InvalidOperatorException(operator);
}
/**
* Resolve a reference to another cell within the spreadsheet. If the other
* cell has a valid value, then the value will be returned, otherwise an
* UnresolvedReferenceException will be thrown.
*
* @param reference
* reference to another cell in the spreadsheet.
* @return value of referenced cell.
* @throws UnresolvedReferenceException
* @throws InvalidValueException
*/
private double resolveCellReference(String reference)
throws UnresolvedReferenceException {
int col = reference.charAt(0) - 'A';
int row = Integer.parseInt(reference.substring(1)) - 1;
Cell referencedCell = spreadsheet.getCell(row, col);
try {
return referencedCell.getValue();
} catch (InvalidValueException e) {
throw new UnresolvedReferenceException();
}
}
/**
* Determine whether a term in an expression is a reference to another cell.
*
* @param term
* @return whether term is a cell reference.
*/
private boolean isCellReference(String term) {
return Character.isLetter(term.charAt(0));
}
/**
* Thrown to indicate than an invalid operator is specified in cell
* expression.
*/
public class InvalidOperatorException extends Exception {
private static final long serialVersionUID = 1L;
private String operator;
public InvalidOperatorException(String operator) {
this.operator = operator;
}
public String toString() {
return "Invalid operator " + operator;
}
}
/**
* Thrown to indicate that a cell reference cannot be resolved. This occurs
* if a valid value is not currently available for the referenced cell.
*/
public class UnresolvedReferenceException extends Exception {
private static final long serialVersionUID = 1L;
}
/**
* Thrown to indicate that getValue() was called on a cell with a value that
* is currently invalid.
*/
public class InvalidValueException extends Exception {
private static final long serialVersionUID = 1L;
}
}
/**
* This class encapsulates a single cell within a spreadsheet.
*/
public class Cell {
private SpreadSheet spreadsheet; // spreadsheet to which cell belongs,
// necessary for resolving cell
// references
private String expression; // expression within cell in reverse Polish
// notation
private double value; // numerical value of evaluating expression
private boolean valueValid; // whether a valid value has been found so
// far. This will change from false to true
// once all cell references have been
// resolved, provided that there are no
// circular references
/**
* Constructor for a cell belonging to a particular spreadsheet.
*
* @param spreadsheet
* SpreadSheet to which Cell belongs.
* @param expression
* expression within cell.
*/
public Cell(SpreadSheet spreadsheet, String expression) {
this.spreadsheet = spreadsheet;
this.expression = expression;
}
/**
* Evaluates expression within a cell. Expression must be in reverse Polish
* notation.
*
* @throws InvalidOperatorException
* if cell expression contains an invalid operator.
* @throws InvalidValueException
* if cell expression contains a reference to a cell that does
* not currently have a valid value.
*/
public void evaluate() throws InvalidOperatorException {
if (!valueValid) { // prevent reevaluation of cells that have valid
// values
try {
// create stack containing term in expression
Deque<String> expressionStack = new ArrayDeque<String>(
Arrays.asList(expression.split("\\s")));
value = evaluateRpn(expressionStack);
valueValid = true;
} catch (UnresolvedReferenceException e) {
// no action is necessary if a reference is unresolved, since it
// may be resolved at a later iteration during the solution of
// the spreadsheet
}
}
}
/**
* Get value of a cell. This is the numerical value resulting from
* evaluating the cell's expression.
*
* @return numerical value resulting from evaluating cell's expression.
* @throws InvalidValueException
* if a valid value is not currently available.
*/
public double getValue() throws InvalidValueException {
if (isValueValid()) {
return value;
} else {
throw new InvalidValueException();
}
}
/**
* Check if a valid numerical value has been evaluated for the cell. This
* will be true when evaluate() is called on the cell and all of the cell
* references in the cell's expression can be resolved.
*
* @return whether cell value is valid.
*/
public boolean isValueValid() {
return valueValid;
}
/**
* Evaluate an expression contained within an ExpressionStack.
*
* @param expressionStack
* an expression represented as a stack of individual terms.
* @return evaluation of expression
* @throws InvalidOperatorException
* @throws UnresolvedReferenceException
* @throws InvalidValueException
*/
private double evaluateRpn(Deque<String> expressionStack)
throws InvalidOperatorException, UnresolvedReferenceException {
String term = expressionStack.removeLast();
if (isCellReference(term)) {
// if last term in expression is a cell reference then resolve it
return resolveCellReference(term);
} else {
double x, y;
try {
// if last term in expression is double then return it
x = Double.parseDouble(term);
} catch (NumberFormatException e) {
// otherwise last term is an operator, evaluate operands and
// apply operator
y = evaluateRpn(expressionStack);
x = evaluateRpn(expressionStack);
x = applyOperator(x, y, term);
}
return x;
}
}
/**
* Apply operator to operands x and y.
*
* @param x
* first operand.
* @param y
* second operand.
* @param operator
* @return result of operation
* @throws InvalidOperatorException
*/
private double applyOperator(double x, double y, String operator)
throws InvalidOperatorException {
if (operator.equals("+"))
return x + y;
else if (operator.equals("-"))
return x - y;
else if (operator.equals("*"))
return x *= y;
else if (operator.equals("/"))
return x / y;
else
throw new InvalidOperatorException(operator);
}
/**
* Resolve a reference to another cell within the spreadsheet. If the other
* cell has a valid value, then the value will be returned, otherwise an
* UnresolvedReferenceException will be thrown.
*
* @param reference
* reference to another cell in the spreadsheet.
* @return value of referenced cell.
* @throws UnresolvedReferenceException
* @throws InvalidValueException
*/
private double resolveCellReference(String reference)
throws UnresolvedReferenceException {
int col = reference.charAt(0) - 'A';
int row = Integer.parseInt(reference.substring(1)) - 1;
Cell referencedCell = spreadsheet.getCell(row, col);
try {
return referencedCell.getValue();
} catch (InvalidValueException e) {
throw new UnresolvedReferenceException();
}
}
/**
* Determine whether a term in an expression is a reference to another cell.
*
* @param term
* @return whether term is a cell reference.
*/
private boolean isCellReference(String term) {
return Character.isLetter(term.charAt(0));
}
/**
* Thrown to indicate than an invalid operator is specified in cell
* expression.
*/
public class InvalidOperatorException extends Exception {
private static final long serialVersionUID = 1L;
private String operator;
public InvalidOperatorException(String operator) {
this.operator = operator;
}
public String toString() {
return "Invalid operator " + operator;
}
}
/**
* Thrown to indicate that a cell reference cannot be resolved. This occurs
* if a valid value is not currently available for the referenced cell.
*/
public class UnresolvedReferenceException extends Exception {
private static final long serialVersionUID = 1L;
}
/**
* Thrown to indicate that getValue() was called on a cell with a value that
* is currently invalid.
*/
public class InvalidValueException extends Exception {
private static final long serialVersionUID = 1L;
}
}
/**
* This class encapsulates a single cell within a spreadsheet.
*/
public class Cell {
private SpreadSheet spreadsheet; // spreadsheet to which cell belongs,
// necessary for resolving cell
// references
private String expression; // expression within cell in reverse Polish
// notation
private double value; // numerical value of evaluating expression
private boolean valueValid; // whether a valid value has been found so
// far. This will change from false to true
// once all cell references have been
// resolved, provided that there are no
// circular references
/**
* Constructor for a cell belonging to a particular spreadsheet.
*
* @param spreadsheet
* SpreadSheet to which Cell belongs.
* @param expression
* expression within cell.
*/
public Cell(SpreadSheet spreadsheet, String expression) {
this.spreadsheet = spreadsheet;
this.expression = expression;
}
/**
* Evaluates expression within a cell. Expression must be in reverse Polish
* notation.
*
* @throws InvalidOperatorException
* if cell expression contains an invalid operator.
*/
public void evaluate() throws InvalidOperatorException {
if (!valueValid) { // prevent reevaluation of cells that have valid
// values
try {
// create stack containing terms in expression
Deque<String> expressionStack = new ArrayDeque<String>(
Arrays.asList(expression.split("\\s")));
value = evaluateRpn(expressionStack);
valueValid = true;
} catch (UnresolvedReferenceException e) {
// no action is necessary if a reference is unresolved, since it
// may be resolved at a later iteration during the solution of
// the spreadsheet
}
}
}
/**
* Get value of a cell. This is the numerical value resulting from
* evaluating the cell's expression.
*
* @return numerical value resulting from evaluating cell's expression.
* @throws InvalidValueException
* if a valid value is not currently available.
*/
public double getValue() throws InvalidValueException {
if (isValueValid()) {
return value;
} else {
throw new InvalidValueException();
}
}
/**
* Check if a valid numerical value has been evaluated for the cell. This
* will be true when evaluate() is called on the cell and all of the cell
* references in the cell's expression can be resolved.
*
* @return whether cell value is valid.
*/
public boolean isValueValid() {
return valueValid;
}
/**
* Evaluate an expression contained within an ExpressionStack.
*
* @param expressionStack
* an expression represented as a stack of individual terms.
* @return evaluation of expression
* @throws InvalidOperatorException
* @throws UnresolvedReferenceException
*/
private double evaluateRpn(Deque<String> expressionStack)
throws InvalidOperatorException, UnresolvedReferenceException {
String term = expressionStack.removeLast();
if (isCellReference(term)) {
// if last term in expression is a cell reference then resolve it
return resolveCellReference(term);
} else {
double x, y;
try {
// if last term in expression is double then return it
x = Double.parseDouble(term);
} catch (NumberFormatException e) {
// otherwise last term is an operator, evaluate operands and
// apply operator
y = evaluateRpn(expressionStack);
x = evaluateRpn(expressionStack);
x = applyOperator(x, y, term);
}
return x;
}
}
/**
* Apply operator to operands x and y.
*
* @param x
* first operand.
* @param y
* second operand.
* @param operator
* @return result of operation
* @throws InvalidOperatorException
*/
private double applyOperator(double x, double y, String operator)
throws InvalidOperatorException {
if (operator.equals("+"))
return x + y;
else if (operator.equals("-"))
return x - y;
else if (operator.equals("*"))
return x *= y;
else if (operator.equals("/"))
return x / y;
else
throw new InvalidOperatorException(operator);
}
/**
* Resolve a reference to another cell within the spreadsheet. If the other
* cell has a valid value, then the value will be returned, otherwise an
* UnresolvedReferenceException will be thrown.
*
* @param reference
* reference to another cell in the spreadsheet.
* @return value of referenced cell.
* @throws UnresolvedReferenceException
*/
private double resolveCellReference(String reference)
throws UnresolvedReferenceException {
int col = reference.charAt(0) - 'A';
int row = Integer.parseInt(reference.substring(1)) - 1;
Cell referencedCell = spreadsheet.getCell(row, col);
try {
return referencedCell.getValue();
} catch (InvalidValueException e) {
throw new UnresolvedReferenceException();
}
}
/**
* Determine whether a term in an expression is a reference to another cell.
*
* @param term
* @return whether term is a cell reference.
*/
private boolean isCellReference(String term) {
return Character.isLetter(term.charAt(0));
}
/**
* Thrown to indicate than an invalid operator is specified in cell
* expression.
*/
public class InvalidOperatorException extends Exception {
private static final long serialVersionUID = 1L;
private String operator;
public InvalidOperatorException(String operator) {
this.operator = operator;
}
public String toString() {
return "Invalid operator " + operator;
}
}
/**
* Thrown to indicate that a cell reference cannot be resolved. This occurs
* if a valid value is not currently available for the referenced cell.
*/
public class UnresolvedReferenceException extends Exception {
private static final long serialVersionUID = 1L;
}
/**
* Thrown to indicate that getValue() was called on a cell with a value that
* is currently invalid.
*/
public class InvalidValueException extends Exception {
private static final long serialVersionUID = 1L;
}
}