This page will examine some modifications we made to our previous code, mainly to improve readability and maintainability. The changes listed here are not the only changes we’ll make to the existing code. Additional changes related to new features and functionality we’re adding in this lesson will be discussed as needed. Below, we’ll look at the Equation interface and classes Exp4jEquation, JEPEquation, GraphPropertySet, and FileManager.
GitHub repository: Cartesian Plane Part 17
Previous page: Cartesian Plane Lesson 17 Page 1:
Refactoring
Equation Facility
See also Updating the Equation Facility on page 3.
In order to make it easier for clients of the Equation facility to work with text entry, we’ve added the abstract method isValidExpression(String exprStr). This method will return true if the given expression is valid: boolean isValidExpression( String expr );
We’ve also made two abstract methods into default methods: isValidName and isValidValue. This is because, as abstract methods, they were implemented in Exp4jEquation and JEPEquation using identical code. The code has been removed from the concrete classes and moved to a single place, making maintenance easier and making these methods easily available to other classes in our project. Along with isValidName, we also have two private helper methods: isAlpha and isAlphaNumeric. The code is identical to what we had before, so there is no need to go over it in detail. However, this is the first time in this project that we have written default methods and private helper methods in an interface, so here is a sketch of what they look like in Equation.java:
public interface Equation
{
// ...
default boolean isValidValue(String valStr)
{
Optional<Double> result = evaluate( valStr );
return result.isPresent();
}
default boolean isValidName( String name )
{
boolean status = false;
// ...
return status;
}
private static boolean isAlpha( char ccc )
{
boolean result =
// ...
return result;
}
private static boolean isAlphanumeric( int ccc )
{
boolean result =
// ...
return result;
}
// ...
}
⏹ Class Exp4jEquation
⏹ Class JEPEquation
Per the above:
- Methods setParam and getParam have been changed to setParamName and getParamName.
- The public methods isValidName and isValidValue have been deleted.
- The helper methods isAlpha and isAlphanumeric have been deleted.
- As the Equation interface requires, isValidExpression has been implemented; find the code below.
public class Exp4jEquation implements Equation
{
// ...
@Override
public boolean isValidExpression( String exprStr )
{
ValidationResult exp4jResult = ValidationResult.SUCCESS;
try
{
Expression expr = new ExpressionBuilder( exprStr )
.variables( vars.keySet() )
.functions( Exp4jFunctions.getFunctions() )
.build();
exp4jResult = expr.validate( false );
}
catch ( Exception exc )
{
List<String> errors = List.of( exc.getMessage() );
exp4jResult = new ValidationResult( false, errors );
}
boolean status = exp4jResult.isValid();
return status;
}
// ...
}
About Exp4jEquation.isValidExpression( String exprStr): due to an issue in the exp4j implementation that we noted in an earlier lesson if exprStr is not a valid expression, we will probably never get to the line reading expr4jResult = expr.validate( false ); the ExpressionBuilder will likely throw an exception first.
public class JEPEquation implements Equation
{
// ...
@Override
public boolean isValidExpression( String exprStr )
{
JEP parser = newParser();
parser.parseExpression( exprStr );
boolean status = !parser.hasError();
return status;
}
// ...
}
Class GraphPropertySet
We have one minor tweak to this class. We have to add a setter and getter for the font style. The code for these methods follows; note that the setter takes a string (the type stored in the PropertyManager), and the getter returns an int (the type stored in a Font object).
public void setFontStyle( String style )
{
fontStyle = style;
}
public int getFontStyle()
{
int style = Font.PLAIN;
if ( isBold() )
style |= Font.BOLD;
if ( isItalic() )
style |= Font.ITALIC;
return style;
}
Class FileManager
See also The FileManager Class on page 3.
To make it easier for FileManager clients to evaluate the results of Open, Save, and Save As operations, we’ve added two public methods to the class:
- Method public static boolean getLastResult will return the result of the previous operation: true if the operation completed normally, false if the operation failed, or the operator canceled the operation. Implementing this operation will require us to add a class variable to FileManager.
- Method public static File getLastFile() will return a reference to the last file that was operated on. Nothing must be added to the class’s state (a class or instance variable) because we can always get this information from the encapsulated JFileChooser.
The implementation of the methods themselves is trivial; you can find them below. Afterward, we will examine the changes to other methods we need to make to complete the implementation.
public class FileManager
{
// ...
private static boolean lastResult = false;
// ...
public static File getLastFile()
{
File file = chooser.getSelectedFile();
return file;
}
public static boolean getLastResult()
{
return lastResult;
}
// ...
}
Any method that produces a result will need to be modified. Following is a summary of the changes.
// ...
public static void save( Equation equation )
{
int action = chooser.showSaveDialog( null );
lastResult = false;
if ( action == JFileChooser.APPROVE_OPTION )
{
lastResult = true;
save( chooser.getSelectedFile(), equation );
}
}
public static void save( File file, Equation equation )
{
try ( PrintWriter pWriter = new PrintWriter( file ) )
{
save( pWriter, equation );
lastResult = true;
}
catch ( IOException exc )
{
// ...
lastResult = false;
}
public static void save( PrintWriter pWriter, Equation equation )
throws IOException
{
// ...
lastResult = true;
}
public static Equation open()
{
int action = chooser.showOpenDialog( null );
Equation equation = null;
if ( action == JFileChooser.APPROVE_OPTION )
{
equation = open( chooser.getSelectedFile() );
}
lastResult = equation != null;
return equation;
}
}
public static Equation open( BufferedReader bufReader )
throws IOException
{
InputParser parser = new InputParser();
CommandReader reader = new CommandReader( bufReader );
reader.stream().forEach( parser::parseInput );
Equation equation = parser.getEquation();
lastResult = true;
return equation;
}
// ...
Summary
On this page, we tweaked some existing code to improve its functionality. On the next page, we’ll examine modifications that we need to make before introducing new features to our application.