The irresistable source.

Lab 3

Description:

This is a Java application to simulate and analyze temperature and humidity data. The application takes advantage of inheritance to avoid duplication of code and improve maintainability and flexibility. The Record class from the previous lab was modified so that it encapsulates a linked list of Measurements, which is a supertype of Temperature, so that it may be used to record statistics for both of the measurements required for this lab as well as any measurements which may be required in the future. With similar motivation, a Sensor class was created to be the superclass of TemperatureSensor and HumiditySensor. Also, the Temperature class was modified so that it no longer converts it’s value field to degrees Celsius behind the scenes; it didn’t appear to save as much code as intended and had the downside of not being as intuitive as keeping the value field exactly as created.

Code:

package jwarren.lab3;

public class HumiditySensor extends Sensor{

	//Humidity Constants
	public static final int LOW_HUMIDITY_LOWER_BOUND = 0;
	public static final int LOW_HUMIDITY_UPPER_BOUND = 50;
	public static final int HIGH_HUMIDITY_LOWER_BOUND = 50;
	public static final int HIGH_HUMIDITY_UPPER_BOUND = 100;

	public HumiditySensor(){

		super();
	}

	public int getHumidity(){

		setReading();

		return getReading();
	}

	public int getLowHumidity(){

		setReading(LOW_HUMIDITY_UPPER_BOUND);

		return getReading();
	}

	public int getHighHumidity(){

		setReading(HIGH_HUMIDITY_LOWER_BOUND, HIGH_HUMIDITY_UPPER_BOUND);

		return getReading();
	}

}
package jwarren.lab3;

public class Measurement implements Comparable{

	private double value;

	public Measurement(double d){

		setValue(d);
	}

	public Measurement(Measurement copy){

		value = copy.getValue();
	}

	public void setValue(double newValue){

		value = newValue;
	}

	//
	//Accessor for the value of this datum.
	//
	public double getValue(){

		return value;
	}	

	//
	//Return a Measurement whose value is the sum of this and rightOp.
	//
	public Measurement add(Measurement rightOp){

		return new Measurement(value + rightOp.getValue());
	}

	//
	//Compares this datum to another and returns:
	//-1 if less than
	//1 if greater than
	//0 if equal
	public int compareTo(Measurement d){

		int compare;

		if(d == null){
			throw new NullPointerException();
		}

		else if(value < d.getValue()){ 
                        compare = -1;
 		}
                else if(value > d.getValue()){
			compare = 1;
		}

		else{
			compare = 0;
		}

		return compare;
	}

	//
	//A string representation of this datum.
	//
	@Override
	public String toString(){

		return Double.toString(value);
	}
}
package jwarren.lab3;
import java.util.LinkedList;

//
//This class maintains a record of temperatures and related statistics.
//
public class Record extends LinkedList{

	/**
	 * 
	 */
	private static final long serialVersionUID = -9095126012449179697L;

	//
	//Returns the highest T in this record.
	//
	public T getHighest(){

		T highest = null;

		for(T measurement : this){

			if( (highest == null) || ( highest.compareTo(measurement) < 0  ) ){ 				highest = measurement; 			} 		} 		 		return highest; 	} 	 	// 	//Returns the lowest T in this record. 	// 	public T getLowest(){ 		 		T lowest = null; 		 		for(T measurement : this){ 			 			if( (lowest == null) || ( lowest.compareTo(measurement) > 0 ) ){
				lowest = measurement;
			}
		}

		return lowest;
	}

	//
	//Returns the sum of all T in this record.
	//
	public Measurement getSum(){

		Measurement sum = null;

		for(T measurement : this){

			if(sum != null){
				sum = sum.add(measurement);
			}

			else{
				sum = measurement;
			}
		}

		return sum;
	}

	//
	//Returns the avg of all T in this record.
	//
	public Measurement getAvg(){

		Measurement sum = getSum();
		double avg = sum.getValue() / size();

		return new Measurement(avg);
	}

}
package jwarren.lab3;

import java.util.Random;

public class Sensor {

	private final int DEFAULT_MAX_VALUE = 100;
	private int reading;
	private Random rng;

	//
	//Default Constructor
	//
	public Sensor(){

		rng = new Random();
		reading = rng.nextInt();
	}

	public int getReading(){

		return reading;
	}

	public void setReading(){

		setReading(DEFAULT_MAX_VALUE);
	}

	//
	//Generates a new reading with a random value between 0 and upperBound.
	//
	public void setReading(int upperBound){

		reading = rng.nextInt(upperBound + 1);
	}

	//
	//Generates a new reading with a random value between lowerBound and upperBound.
	//
	public void setReading(int lowerBound, int upperBound){

		reading = lowerBound + rng.nextInt(upperBound - lowerBound + 1);
	}

}
package jwarren.lab3;
import java.util.LinkedList;
import java.util.Random;

//
//A temperature sensor simulation app.
//
public class SensorStatsApp {

	private static enum MenuOption{
		WINTER(1), SPRING(2), SUMMER(3), FALL(4), RANDOM(5), EXIT(6);

		private final int value;

		MenuOption(int n){

			value = n;
		}

		//Returns the numerical value of the enum.
		public int getValue(){

			return value;
		}

		//Returns the enum who's numerical value is equal to v.
		public static MenuOption fromValue(int v){

			MenuOption option;

			switch(v){

			case 1:
				option = MenuOption.WINTER;
				break;
			case 2:
				option = MenuOption.SPRING;
				break;
			case 3:
				option = MenuOption.SUMMER;
				break;
			case 4:
				option = MenuOption.FALL;
				break;
			case 5:
				option = MenuOption.RANDOM;
				break;
			case 6:
				option = MenuOption.EXIT;
				break;
			default:
				option = null;
			}

			return option;
		}

	}

	private static enum HumidityOption{

		FULLRANGE(1), LOW(2), HIGH(3), RANDOM(4), EXIT(5);

		private final int value;

		HumidityOption(int v){

			value = v;
		}

		public int getValue(){
			return value;
		}

		//Returns the enum who's numerical value is equal to v.
		public static HumidityOption fromValue(int v){

			HumidityOption option;

			switch(v){

				case 1:
					option = HumidityOption.FULLRANGE;
					break;
				case 2:
					option = HumidityOption.LOW;
					break;
				case 3:
					option = HumidityOption.HIGH;
					break;
				case 4:
					option = HumidityOption.RANDOM;
					break;
				case 5:
					option = HumidityOption.EXIT;
					break;
				default:
					option = null;
				}

				return option;
			}
	}

	public static void main(String args[]){

		boolean done = false;
		boolean randomSeason = false;
		boolean randomHumidity = false;
		int userInput;
		int numSimulations = 0;
		MenuOption selection = MenuOption.EXIT;
		HumidityOption humidSelection = HumidityOption.EXIT;
		LinkedList<Record<?>> records;
		Record tempRecord;
		Record humidRecord;
		TemperatureSensor tempSensor = new TemperatureSensor();
		HumiditySensor humidSensor = new HumiditySensor();
		TextUI ui = new TextUI(System.in, System.out);
		int randomNum;
		Random rng;

		//Display the welcome message.
		ui.println("Welcome to Season Sim®");

		do{
			//Display menu to user and get input
			try{
				ui.println("Select one of the following simulations:");
				ui.println("<"+MenuOption.WINTER.getValue()+"> for Winter simulation.");
				ui.println("<"+MenuOption.SPRING.getValue()+"> for Spring simulation.");
				ui.println("<"+MenuOption.SUMMER.getValue()+"> for Summer simulation.");
				ui.println("<"+MenuOption.FALL.getValue()+"> for Fall simulation.");
				ui.println("<"+MenuOption.RANDOM.getValue()+"> for a Random simulation.");
				ui.println("or <"+MenuOption.EXIT.getValue()+"> to exit the program.");
				ui.print(": ");
				userInput = ui.readInt(1,MenuOption.EXIT.getValue());

				//Selection will be null if userInput is not valid enum value.
				selection = MenuOption.fromValue(userInput);

				//This should not happen unless TextUI is broken.
				if(selection == null){

					//Exit
					done = true;
				}

				else if(selection != MenuOption.EXIT){

					//Display Humidity Options.
					ui.println("Select type of humidity:");
					ui.println("<"+HumidityOption.FULLRANGE.getValue()+"> Full Range");
					ui.println("<"+HumidityOption.LOW.getValue()+"> Low");
					ui.println("<"+HumidityOption.HIGH.getValue()+"> High");
					ui.println("<"+HumidityOption.RANDOM.getValue()+"> Random");
					ui.println("<"+HumidityOption.EXIT.getValue()+"> Exit");
					ui.print(": ");
					userInput = ui.readInt(1, HumidityOption.EXIT.getValue());

					humidSelection = HumidityOption.fromValue(userInput);

					if(humidSelection == null){
						//Exit
						done = true;
					}

					else if(humidSelection != HumidityOption.EXIT){

						//Ask how man simulations to run.
						ui.print("Enter the number of simulations to run: ");
						numSimulations = ui.readInt(0,Integer.MAX_VALUE);

						if(selection == MenuOption.RANDOM){

							rng = new Random();
							//Generate a random number between 1 and the last Season value.
							randomNum = 1 + rng.nextInt(MenuOption.RANDOM.getValue()-1);
							selection = MenuOption.fromValue(randomNum);
							randomSeason = true;
						}

						else{

							randomSeason = false;
						}

						if(humidSelection == HumidityOption.RANDOM){

							rng = new Random();
							//Generate a random number between 1 and the last Humidity val.
							randomNum = 1 + rng.nextInt(HumidityOption.RANDOM.getValue()-1);
							humidSelection = HumidityOption.fromValue(randomNum);
							randomHumidity = true;
						}

						else{
							randomHumidity = false;
						}

						records = runSimulations(numSimulations, selection, humidSelection,
								tempSensor,	humidSensor);

						if(records != null && records.size() != 0){

							//Display Temperature Statistics to user.
							ui.print("Season:");

							if(randomSeason){
								ui.print("Random:");
							}
							ui.println(selection);
							tempRecord = (Record) records.getFirst();
							humidRecord = (Record) records.getLast();

							ui.println("First Temperature: " + tempRecord.getFirst());
							ui.println("Last Temperature: " + tempRecord.getLast());
							ui.println("Lowest Temperature: " + tempRecord.getLowest());
							ui.println("Highest Temperature: " + tempRecord.getHighest());
							ui.println("Sum of Temperatures: " + tempRecord.getSum());
							ui.println("Average Temperature: " + tempRecord.getAvg());
							ui.println("");
							ui.print("Humidity type:");
							if(randomHumidity){

								ui.print("Random:");
							}
							ui.println(humidSelection);
							ui.println("First Humidity: " + humidRecord.getFirst());
							ui.println("Last Humidity: " + humidRecord.getLast());
							ui.println("Lowest Humidity: " + humidRecord.getLowest());
							ui.println("Highest Humidty: " + humidRecord.getHighest());
							ui.println("Total sum of Humidity: " + humidRecord.getSum());
							ui.println("Average Humidity: " + humidRecord.getAvg());
							ui.println("");
						}
					}
				}

				else{
					ui.print("Goodbye.\n");
					done = true;
				}
			}

			catch(TextUI.TextUIException e){
				ui.print(e.getMessage() + "\n");
				ui.print("Please try again...\n\n");
				done = false;
			}

		}while(!done);

	}

	//
	//Runs a specified number of Temperature simulations and returns 
	//a tempRecord of the runs.
	//
	private static LinkedList<Record<?>> runSimulations(final int numSims, 
			MenuOption selection, HumidityOption humidSelection,
			TemperatureSensor tempSensor, HumiditySensor humidSensor){

		double temp = 0;
		double humid = 0;
		Record tempRecord = new Record();
		Record humidRecord = new Record();
		LinkedList<Record<?>> records = new LinkedList<Record<?>>();

		for(int i=0; i<numSims; i++){

			switch(selection){

			case WINTER:
				temp = tempSensor.getWinterTemp();
				break;
			case SPRING:
				temp = tempSensor.getSpringTemp();
				break;
			case SUMMER:
				temp = tempSensor.getSummerTemp();
				break;
			case FALL:
				temp = tempSensor.getFallTemp();
				break;
			default:
				tempRecord = null;
			}

			switch(humidSelection){

			case LOW:
				humid = humidSensor.getLowHumidity();
				break;
			case HIGH:
				humid = humidSensor.getHighHumidity();
				break;
			case FULLRANGE:
				humid = humidSensor.getHumidity();
				break;
			default:
				humidSensor = null;
			}

			if(tempRecord != null && humidRecord != null){
				tempRecord.add(new Temperature(temp,Temperature.Units.FAHRENHEIT));
				humidRecord.add(new Measurement(humid));
			}
		}

		records.add(tempRecord);
		records.add(humidRecord);

		return records;

	}

}
package jwarren.lab3;
import java.text.DecimalFormat;

//
//This class implements a temperature data type.
//
public class Temperature extends Measurement{

	public enum Units {
		CELSIUS, FAHRENHEIT, KELVIN;
	};

	public static class InvalidTemperatureUnitException extends Exception{

		/**
		 * 
		 */
		private static final long serialVersionUID = 3755108601677734615L;

		InvalidTemperatureUnitException(String message){
			super(message);
		}
	}

	private Units units;		//Unit's to display

	//Default Constructor
	public Temperature(){

		super(0.0);
		setUnits(Units.CELSIUS);
	}

	//
	//Copy Constructor
	//
	public Temperature(Temperature copy){

		super(copy.getValue());
		String newUnits = copy.getUnits();

		setUnits(newUnits);
	}

	//
	//Constructor sets the temperature to the parameter.
	//
	public Temperature(double value, Units newUnits){

		super(value);
		setUnits(newUnits);
	}

	//
	//Accessor.
	//
	public String getUnits(){
		return units.toString();
	}

	//
	//Mutator for units.
	//
	public void setUnits(Units newUnits){

		units = newUnits;
	}

	public void setUnits(String newUnits){

		if(newUnits.toUpperCase() == "CELSIUS"){

			setUnits(Units.CELSIUS);
		}

		else if(newUnits.toUpperCase() == "FAHRENHEIT"){

			setUnits(Units.FAHRENHEIT);
		}

		else{
			setUnits(Units.KELVIN);
		}
	}

	//
	//Sets the temperature to newTemp.
	//Note: newTemp is assumed to be in units of newUnits.
	//
	public void setTemperature(double newTemp, Units newUnits){

		setValue(newTemp);
		units = newUnits;
	}

	//
	//Sets the temperature to newTemp.
	//Note: newTemp is assumed to be in units of stringNewUnits.
	//
	public void setTemperature(double newTemp, String stringNewUnits) 
			throws InvalidTemperatureUnitException{

		try{
			Units  newUnits = Units.valueOf(stringNewUnits);
			setTemperature(newTemp, newUnits);
		}

		catch(IllegalArgumentException e){

			String message = "\""+stringNewUnits+"\"" + "is not a valid temperature unit.";
			throw new InvalidTemperatureUnitException(message);
		}
	}

	//
	//Returns the value of temperature in units specified by displalyUnits.
	//
	public Temperature getTemperatureAs(Units displayUnits){

		double newValue;

		switch(displayUnits){

		case FAHRENHEIT:
			if(units == Units.FAHRENHEIT){
				//no conversion necessary.
				newValue = getValue();
			}

			else if(units == Units.CELSIUS){
				newValue = celsiusToFahrenheit(getValue());
			}

			else{
				newValue = kelvinToCelsius(getValue());	//First convert to Celsius.
				newValue = celsiusToFahrenheit(newValue);//Then convert to Fahrenheit.
			}			
			break;
		case KELVIN:
			if(units == Units.KELVIN){
				newValue = getValue();
			}

			else if(units == Units.FAHRENHEIT){
				newValue = fahrenheitToCelsius(getValue());
				newValue = celsiusToKelvin(newValue);
			}
			else{
				newValue = celsiusToKelvin(getValue());
			}
			break;
		//Celsius case.
		default:
			if(units == Units.CELSIUS){
				newValue = getValue();
			}

			else if(units == Units.FAHRENHEIT){
				newValue = fahrenheitToCelsius(getValue());
			}

			else{
				newValue = kelvinToCelsius(getValue());
			}
		}

		return new Temperature(newValue,displayUnits);
	}

	//
	//Returns a temperature whose value is the sum of this and rightOp and has the same units
	//as this.
	//
	public Temperature add(Temperature rightOp){

		//Compute the sum using the units of left operand.
		double sum = this.getValue() + rightOp.getTemperatureAs(units).getValue();

		return new Temperature(sum, units);
	}

	//
	//A string representation of this.
	//
	@Override
	public String toString(){

		String unitsAsString;

		switch(units){

		case FAHRENHEIT:
			unitsAsString = "°F";
			break;
		case KELVIN:
			unitsAsString = "K";
			break;
		default:
			unitsAsString = "°C";
		}

		return new DecimalFormat("##.#").format(getValue()) + unitsAsString;
	}

	//
	//Compares this to another Temperature and returns:
	//-1 if this < t. 	//+1 if this > t.
	//0 if this == t.
	//
	public int compareTo(Temperature t) {

		int compare;

		if(t == null){
			throw new NullPointerException();
		}

		else if(getValue() < t.getTemperatureAs(units).getValue()){ 			 			compare = -1; 		} 		 		else if(getValue() > t.getTemperatureAs(units).getValue()){
			compare = 1;
		}

		else{
			compare = 0;
		}

		return compare;
	}

	private double celsiusToFahrenheit(double celsius){

		double fahrenheit = celsius * 180/100 + 32;

		return fahrenheit;
	}

	private double fahrenheitToCelsius(double fahrenheit){

		double celsius = (fahrenheit - 32) * 100/180;

		return celsius;
	}

	private double celsiusToKelvin(double celsius){

		double kelvin = celsius + 273;

		return kelvin;
	}

	private double kelvinToCelsius(double kelvin){

		double celsius = kelvin - 273;

		return celsius;
	}
}
package jwarren.lab3;

//
//A class to simulate seasonal temperatures.
//
public class TemperatureSensor extends Sensor {

	//Season simulating constants.
	public static final int WINTER_MIN = 20;
	public static final int WINTER_MAX = 40;
	public static final int SPRING_MIN = 40;
	public static final int SPRING_MAX = 70;
	public static final int SUMMER_MIN = 70;
	public static final int SUMMER_MAX = 90;
	public static final int FALL_MIN = 40;
	public static final int FALL_MAX = 60;

	//
	//Default Constructor
	//
	public TemperatureSensor(){

		super();
	}

	//
	//Returns a random number between WINTER_MIN and WINTER_MAX (inclusive).
	//
	public int getWinterTemp(){

		setReading(WINTER_MIN, WINTER_MAX);

		return getReading();

	}

	//
	//Returns a random number between SPRING_MIN and SPRING_MAX (inclusive).
	//
	public int getSpringTemp(){

		setReading(SPRING_MIN, SPRING_MAX);

		return getReading();
	}

	//
	//Returns a random number between SUMMER_MIN and SUMMER_MAX (inclusive).
	//
	public int getSummerTemp(){

		setReading(SUMMER_MIN, SUMMER_MAX);

		return getReading();
	}

	//
	//Returns a random number between FALL_MIN and FALL_MAX (inclusive).
	//
	public int getFallTemp(){

		setReading(FALL_MIN, FALL_MAX);

		return getReading();
	}
}
package jwarren.lab3;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.InputMismatchException;
import java.util.Scanner;

public class TextUI {

	private Scanner in;
	private PrintStream out;

	//
	//Exception super type thrown by this class.
	//
	public static class TextUIException extends Exception{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		TextUIException(String msg){
			super(msg);
		}

	}

	//
	//Exception thrown when UI encounters an invalid input.
	//
	public static class InvalidInputException extends TextUIException{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		InvalidInputException(String msg){

			super(msg);
		}

	}

	//
	//Exception thrown when a non computable range is encountered
	//
	public static class InvalidInputRangeException extends TextUIException{

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		InvalidInputRangeException(String msg){

			super(msg);
		}
	}

	//
	//Initialization Constructor.
	//
	public TextUI(InputStream iStream, OutputStream oStream){

		in = new Scanner(iStream);
		out = new PrintStream(oStream);
	}

	//
	//Prints to the output stream.
	//
	public void print(Object msg){

		out.print(msg);
	}

	public void println(Object msg){

		print(msg);
		print("\n");
	}

	//
	//Reads an integer from the output stream. This method will return a value between
	//min - max (inclusive) or thrown an InvalidInputException.
	//
	public int readInt(int min, int max) throws TextUIException{

		int input = 0;

		if(max < min){

			throw new InvalidInputRangeException("Max value cannot be set below minimum.");
		}

		try{

			input = in.nextInt();
		}

		catch(InputMismatchException e){

			in.next();
			throw new InvalidInputException("Must enter an integer.");
		}

		if(input < min || input > max){

			throw new InvalidInputException("\""+input+"\" is not within valid "
					+ "range: " + min + " through " + max + ".");
		}

		return input;
	}
}

Screenshots:

screen 1screen 2

Leave a Reply

Your email address will not be published. Required fields are marked *