/ assign2 / Number.java
Number.java
  1  /*
  2   * SPDX-FileCopyrightText: 2024 Jonas Smedegaard <dr@jones.dk
  3   *
  4   * SPDX-License-Identifier: GPL-3.0-or-later
  5   */
  6  
  7  // leave parsing complexities to a common class.
  8  import java.util.Scanner;
  9  
 10  // let's complicate this task...
 11  import java.util.regex.Matcher;
 12  import java.util.regex.Pattern;
 13  
 14  public class Number {
 15  	public static void main(String[] args) {
 16  		// instantiate object for scanning terminal input
 17  		Scanner input = new Scanner(System.in);
 18  
 19  		// precompile regex for more efficient matching
 20  		Pattern re_number = Pattern.compile("(-)?(\\d+)([,.](\\d+)?)*|(-)?[,.](\\d+)");
 21  		// named groups would be more elegant,
 22  		// but supported properly only since OpenJDK 20,
 23  		// and java is crappy for declaring compatibility level...
 24  		// (note to self: yes, Perl is still relevant today!)
 25  		final int GRP_NEG = 1;
 26  		final int GRP_INT = 2;
 27  		final int GRP_SEP = 3;
 28  		final int GRP_FRAC = 4;
 29  		final int GRP_FRAC_ONLY_NEG = 5;
 30  		final int GRP_FRAC_ONLY = 6;
 31  
 32  		// define variables
 33  		String line;
 34  		float number;
 35  		int matches = 0;
 36  
 37  		// emit empty line before the initial query
 38  		System.out.println();
 39  		while (true) {
 40  			line = requestNumber(input);
 41  			Matcher m = re_number.matcher(line);
 42  			while (m.find()) {
 43  				// emit empty line before each number processed
 44  				System.out.println();
 45  				// reconstruct number from matched components
 46  				number = 0;
 47  				if (m.group(GRP_INT) != null) {
 48  					number += Float.parseFloat(m.group(GRP_INT));
 49  				}
 50  				else if (m.group(GRP_FRAC_ONLY) != null) {
 51  					number += Float.parseFloat("." + m.group(GRP_FRAC_ONLY));
 52  				}
 53  				if (m.group(GRP_FRAC) != null) {
 54  					number += Float.parseFloat("." + m.group(GRP_FRAC));
 55  				}
 56  				if (m.group(GRP_NEG) != null || m.group(GRP_FRAC_ONLY_NEG) != null) {
 57  					number = -number;
 58  				}
 59  				matches++;
 60  				if (matches > 1) {
 61  					System.err.println("NOTICE: Oh, an additional number - this is real fun...");
 62  				}
 63  				examineNumber(number);
 64  			}
 65  			if (matches <= 0) {
 66  				if (!stringIsFun(line)) {
 67  					System.err.println("ERROR: no number provided - exiting!");
 68  					System.exit(0);
 69  				}
 70  			}
 71  			// emit empty line before next query
 72  			System.out.println();
 73  			System.err.println("NOTICE: This is fun - let's try again...");
 74  			matches = 0;
 75  		}
 76  	}
 77  
 78  	public static String requestNumber(Scanner channel) {
 79  		// provide a prompt
 80  		System.out.print("Please enter a number -> ");
 81  
 82  		// capture response as string
 83  		return channel.nextLine();
 84  	}
 85  
 86  	public static void examineNumber(float x) {
 87  		if (x == (int) x) {
 88  			// use ternary operator for more compact code
 89  			System.out.printf("The number is %s.\n", ( x % 2 == 0 ) ? "even" : "uneven");
 90  		} else {
 91  			System.out.println("The number is fractional.");
 92  		}
 93  		System.out.printf("The number is %s.\n", ( x < 0 ) ? "negative" : "positive");
 94  	}
 95  
 96  	// let's add an easter egg...
 97  	public static boolean stringIsFun(String s) {
 98  		// sloppy ad-hoc regex: acceptable to be slow at getting the joke
 99  		if (s.matches("(?i).*\\bnumber\\b.*")) {
100  			System.err.println("ERROR: Ha ha funny, but semantic (not literal) response is expected...");
101  			return true;
102  		} else if (s.matches("(?i).*\\bzero|one|two|three|four|five|six|seven|eight|nine|ten|elleven|twelve|fiften|twenty|thirty|fifty|hundred.*")) {
103  			System.err.println("ERROR: Ha ha clever, but digits are expected...");
104  			return true;
105  		} else {
106  			return false;
107  		}
108  	}
109  }