Computer Science 210
Data Structures

Fall 2019, Siena College

RatioListApplet BlueJ Project

Click here to download a BlueJ project for RatioListApplet.


RatioListApplet Source Code

The Java source code for RatioListApplet is below. Click on a file name to download it.


RatioListApplet.java

// <applet code='RatioListApplet' width='400' height='450'></applet>
/**
 * Example RatioListApplet: a simple Applet to manipulate
 * a RatioList - a list of Ratio objects
 *
 * Run in appletviewer with the provided view.html file
 *
 * Jim Teresco, The College of Saint Rose, CSC 523, Summer 2014
 * Siena College, CSIS 210, Fall 2018
 *
 * @author Jim Teresco
 */

/* tons of imports for the GUI */
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JTextField;

public class RatioListApplet extends JApplet implements ActionListener {

    // GUI components
    private JTextField displayNumerator, displayDenominator;
    private JButton insertButton, sumButton, minButton, maxButton, reduceButton, reduceAllButton, showAllButton;

    // our RatioList, which is null until the first is inserted
    private RatioList ratios;

    /**
       required init method called when the JApplet is initialized
    */
    public void init() {

        Container contentPane = getContentPane();

        // a bigger font for our display label and text fields
        Font biggerFont = new Font("Sans-Serif", Font.BOLD, 24);
        Font biggestFont = new Font("Fixed", Font.PLAIN, 48);

        // a JPanel for the center that displays the "current" ratio
        JPanel currentPanel = new JPanel();
        JLabel currentLabel = new JLabel("Display value:");
        currentLabel.setFont(biggerFont);
        currentPanel.add(currentLabel);
        // another JPanel to create the ratio display
        JPanel ratioPanel = new JPanel();
        ratioPanel.setLayout(new BoxLayout(ratioPanel, BoxLayout.PAGE_AXIS));
        displayNumerator = new JTextField("0", 5);
        displayNumerator.setFont(biggestFont);
        displayNumerator.setHorizontalAlignment(JTextField.CENTER);
        ratioPanel.add(displayNumerator);
        ratioPanel.add(new JSeparator());
        displayDenominator = new JTextField("1", 5);
        displayDenominator.setFont(biggestFont);
        displayDenominator.setHorizontalAlignment(JTextField.CENTER);
        ratioPanel.add(displayDenominator);
        currentPanel.add(ratioPanel);
        contentPane.add(currentPanel, BorderLayout.CENTER);

        // a JPanel for the buttons across the bottom
        JPanel buttonPanel = new JPanel(new GridLayout(0, 1));
        insertButton = setupButton("Insert Current", buttonPanel);
        sumButton = setupButton("Set Current to Sum", buttonPanel);
        minButton = setupButton("Set Current to Min", buttonPanel);
        maxButton = setupButton("Set Current to Max", buttonPanel);
        reduceButton = setupButton("Reduce Current to Lowest Terms", buttonPanel);
        reduceAllButton = setupButton("Reduce All to Lowest Terms", buttonPanel);
        showAllButton = setupButton("Show All Ratios", buttonPanel);
        contentPane.add(buttonPanel, BorderLayout.SOUTH);
        contentPane.validate();
    }

    // a private method to create, insert, and set the listener for our buttons
    private JButton setupButton(String label, JPanel addTo) {

        JButton me = new JButton(label);
        addTo.add(me);
        me.addActionListener(this);
        return me;
    }

    /**
       Check if the contents of the given JTextField represent a valid
       integer value
       
       @param f the JTextField to check
       @return true if the text in f successfully can be parsed as an integer
    */
    private static boolean isValidInt(JTextField f) {

        String text = f.getText();
        try {
            Integer.parseInt(text);
        }
        catch (NumberFormatException e) {
            return false;
        }
        return true;
    }

    // helper method to display a given ratio in the display area
    private void displayRatio(Ratio r) {

        displayNumerator.setText(""+r.getNumerator());
        displayDenominator.setText(""+r.getDenominator());
    }

    // helper method to get a Ratio based on current contents of the text fields,
    // and returns null (after showing error dialog) if the fields are not valid
    // to construct a Ratio
    private Ratio ratioOfDisplay() {
        // we need to take the value on the display and create a new ratio out of it

        // we need to verify that the value displayed is valid to use as a ratio
        // first, check that the text fields have numbers in them
        boolean numValid = isValidInt(displayNumerator);
        boolean denValid = isValidInt(displayDenominator);
        String message = "";
        if (!numValid) {
            message += "Could not parse numerator as an integer value!\n";
        }
        if (!denValid) {
            message += "Could not parse denominator as an integer value!\n";
        }
        if (!numValid || !denValid) {
            JOptionPane.showMessageDialog(null, message, "Invalid Entry", JOptionPane.ERROR_MESSAGE);
            return null;
        }

        // we know they're numbers, so let's get them
        int num = Integer.parseInt(displayNumerator.getText());
        int den = Integer.parseInt(displayDenominator.getText());

        // make sure we're not dividing by zero
        if (den == 0) {
            JOptionPane.showMessageDialog(null, "Denominator cannot be 0", "Invalid Entry", JOptionPane.ERROR_MESSAGE);
            return null;
        }

        // if we get here, things are good, construct and return a Ratio
        return new Ratio(num, den);

    }

    /**
       handle all button presses

       @param e the ActionEvent describing the action that triggered this event
       handler to be called
    */
    public void actionPerformed(ActionEvent e) {

        if (e.getSource() == insertButton) {
            // we need to take the value on the display and create a new ratio out of it
            // and add it to our collection

            Ratio newOne = ratioOfDisplay();
            if (newOne != null) {
                // things are valid, so let's construct a Ratio and make it the new
                // first entry in our RatioList
                ratios = new RatioList(newOne, ratios);
            }
        }
        else if (e.getSource() == showAllButton) {
            // we just want to print out all of the ratios in our collection
            // so we'll put them up in a dialog box
            String message;
            if (ratios == null) {
                message = "No ratios in list.";
            }
            else {
                message = ratios.toString();
            }
            JOptionPane.showMessageDialog(null, message);

        }
        else if (e.getSource() == sumButton) {
            // we need to compute the sum and display it
            if (ratios == null) {
                displayRatio(new Ratio(0,1));
            }
            else {
                displayRatio(ratios.getSum());
            }
        }
        else if (e.getSource() == minButton) {
            if (ratios == null) {
                JOptionPane.showMessageDialog(null, "No ratios in list.");
            }
            else{
                displayRatio(ratios.getMin());
            }
        }
        else if (e.getSource() == maxButton) {
            if (ratios == null) {
                JOptionPane.showMessageDialog(null, "No ratios in list.");
            }
            else {
                displayRatio(ratios.getMax());
            }
        }
        else if (e.getSource() == reduceButton) {
            // get the Ratio from the display text fields
            Ratio displayed = ratioOfDisplay();
            if (displayed != null) {
                displayed.reduce();
                displayRatio(displayed);
            }
        }
        else if (e.getSource() == reduceAllButton) {
            
            if (ratios == null) {
                JOptionPane.showMessageDialog(null, "No ratios in list.");
            }
            else {
                ratios.reduceAll();
            }
        }
    }
}

RatioList.java

/**
 * A recursive data structure (list) of Ratio objects with some
 * useful methods to manage such a list
 *
 * Jim Teresco, The College of Saint Rose, CSC 523, Summer 2014
 * Siena College, CSIS 210, Fall 2018
 *
 * @author Jim Teresco
 */

public class RatioList {

    // our instance variables have room for one Ratio, and
    // then a list of other ratios -- which is exactly
    // the structure we are defining -- so we can define this
    // structure *recursively*
    private Ratio first;
    private RatioList rest;

    /**
       Construct a RatioList from a Ratio and another RatioList,
       which would be null when constructing a brand new RatioList
       with its first element

       @param first the Ratio to become the first element in the list
       @param last the RatioList which will become the rest of the new list
    */
    public RatioList(Ratio first, RatioList rest) {
        
        this.first = first;
        this.rest = rest;
    }

    /**
       compute the sum of all ratios in the list, recursively

       @return a Ratio representing the sum of all Ratios in the list
    */
    public Ratio getSum() {
        
        // if there is no rest, the sum is just first
        if (rest == null) return first;
        
        // otherwise, we have to compute the sum of the rest (using
        // this method) and then add in first, using Ratio's add method
	Ratio restSum = rest.getSum();
	Ratio answer = first.add(restSum);
        return answer;
    }

    /**
       find the minimum ratio, recursively
       
       the minimum is the smaller of first and the minimum of the rest,
       which we compute using this method recursively
       
       @return the Ratio in the list with the smallest value
    */
    public Ratio getMin() {
        
        // the smallest out of a single ratio is that ratio
        if (rest == null) return first;
        
        // otherwise, find the min of the rest and compare to first
        Ratio minOfRest = rest.getMin();
        if (minOfRest.getDecimalValue() < first.getDecimalValue()) {
            return minOfRest;
        }
        else {
            return first;
        }
    }
    
    /**
       find the maximum ratio, recursively
       
       the maximum is the larger of first and the maximum of the rest,
       which we compute using this method recursively
       
       @return the Ratio in the list with the largest value
    */
    public Ratio getMax() {
        
        // the largest out of a single ratio is that ratio
        if (rest == null) return first;
        
        // otherwise, find the max of the rest and compare to first
        Ratio maxOfRest = rest.getMax();
        if (maxOfRest.getDecimalValue() > first.getDecimalValue()) {
            return maxOfRest;
        }
        else {
            return first;
        }
    }
    
    /**
       reduce all Ratios in the list to lowest terms

    */
    public void reduceAll() {
    
        // reduce the one Ratio we have
        first.reduce();
        
        // and if there are more, reduce recursively
        if (rest != null) rest.reduceAll();
    }
    
    /**
       create a String representation of the Ratios in the list

       the toString method is recursive!  To print this whole RatioList,
       we concatenate the result of first's toString with a recursive
       call to this toString method.  But only do the latter when rest
       is not null (we are not at the end of the list)

       @return a String representation of all of the Ratios in the list
    */
    public String toString() {
        
        String answer = first.toString();
        if (rest != null) {
            answer += ", " + rest.toString();
        }
       
        return answer;
    }
}

Ratio.java

/**
 * Example Ratios -- the Ratio class
 *
 * This class encapsulates a numerator and denominator and
 * includes the capability to set the numerator or denominator,
 * retrieve the numerator or denominator, retrieve the decimal
 * equivalent of the ratio, and return a "pretty" String 
 * representation of the ratio
 * 
 * This version includes a non-destructive method to add another
 * ratio to this and return a new ratio that contains the sum
 *
 * Jim Teresco, The College of Saint Rose, CSC 202, Fall 2012
 * CSC 523, Summer 2014, Siena College CSIS 210, Fall 2018
 *
 * @author Jim Teresco
 */

public class Ratio {

    // Instance variables for the two integers, one each for the numerator
    // and denominator.

    private int numerator;
    private int denominator;
    
    // our straightforward constructor
    public Ratio(int num, int den) {
        
        numerator = num;
        denominator = den;
    }
    
    // Mutators for numerator and denominator
    public void setNumerator(int num) {
        
        numerator = num;
    }
    
    public void setDenominator(int den) {
        
        denominator = den;
    }
    
    // And some accessors
    public int getNumerator() {
        
        return numerator;
    }
    
    public int getDenominator() {
        
        return denominator;
    }
    
    public double getDecimalValue() {
        
        return 1.0 * numerator / denominator;
    }
    
    // print our simple string representation
    public String toString() {
        
        return numerator + "/" + denominator;
    }
    
    // a non-destructive add method that adds another ratio to this
    // one and returns a new one (leaving this and other unchanged)
    public Ratio add(Ratio other) {
        
        return new Ratio(numerator*other.denominator + denominator*other.numerator,
                         denominator*other.denominator);
        
    }
    
    // a recursive method to compute greatest common divisor using Euclid's method
    public static int gcd(int a, int b) {
        
        if (a == 0) return b;
        if (a > b) return gcd(b, a);  // so below we can assume b>=a
        return gcd(b%a, a);
    }
    
    // Reduce this ratio to lowest terms.  To do so, we find the greatest common
    // divisor of the numerator and denominator, then divide both
    // numerator and denominator by that value
    public void reduce() {
        
        int divideBy = gcd(numerator, denominator);
        numerator /= divideBy;
        denominator /= divideBy;
    }
}