/** 
 * Dates, with JUnit 3.8. test cases
 *
 * The parsing of date strings is a case analysis; many different countries have different
 * date formats, so every country in which the software shall be sold, generates
 * a case (internationalization). In big Enterprise Software frameworks, dates
 * are big classes with a lot of code!
 */

import java.lang.*;  // Integer
import java.util.*;
import java.util.zip.DataFormatException;

/**
 * @author Uwe Assmann
 * @date 2013-04-28
 * @url http://docs.oracle.com/javase/tutorial/essential/regex/index.html
 */
public class DateD {
    /** Attributes of a Date object */
    public int day = 0; // initialized to 0, i.e., with a wrong postcondition
    public int month = 0;
    public int year = 0;
    /**
       Parse a day from a string in German or English date format
    */
    public int parseDay(String date) throws DataFormatException {
	if (date.equals(""))   throw new DateInvalid(); // precondition
 	if (date.indexOf('.') >=0 ) {
	    // this must be a German date
	    if (date.matches("\\d\\d.\\d\\d.\\d\\d\\d\\d")) {
		// Format: 01.01.2018
		day = Integer.parseInt(date.substring(0,2));
		if (day < 1 || day > 31) throw new DayTooLarge(); // postcondition
	    } else if (date.matches("\\d\\d.\\s\\d\\d.\\s\\d\\d\\d\\d")) {
		// Format: 01. 01. 2018
		day = Integer.parseInt(date.substring(0,2));
		if (day < 1 || day > 31) throw new DayTooLarge(); // postcondition
	    } else {
		// ... fill in other format recognitions...
	    }
	}  
 	if (date.indexOf('/') >=0 ) {
	    // this must be an English date
	    if (date.matches("\\d\\d/\\d\\d/\\d\\d\\d\\d")) {
		// Format: 10/21/2018
		day = Integer.parseInt(date.substring(3,5));
	    } else if (date.matches("\\d\\d/ \\d\\d/ \\d\\d\\d\\d")) {
		// Format: 10/ 21/ 2018
		day = Integer.parseInt(date.substring(4,6));
	    } else {
		// ... fill in other format recognitions...
	    }
	}  
	if (day > 31) throw new DayTooLarge(); // postcondition
	if (day < 1) throw new DateWrong(date); // postcondition
	return day;
    }
    /**
     * Parse a month in German or English position.
     */
    public int parseMonth(String date) throws DataFormatException {
 	int firstPoint = date.indexOf('.');
 	if (firstPoint >= 0) { // this must be a German date; day is first
	    // cut off day
	    String monthYearString = date.substring(firstPoint+1); 
	    System.out.println("**test German month**:"+monthYearString);
	    if (monthYearString.matches("\\d\\d.\\d\\d\\d\\d")) {
		// Format: 01.2018
		month = Integer.parseInt(monthYearString.substring(0,2));
		if (month < 1 || month > 12) throw new MonthTooLarge(); // postcondition
	    } else if (monthYearString.matches("\\s\\d\\d.\\s\\d\\d\\d\\d")) {
		// Format:  01. 2018
		month = Integer.parseInt(monthYearString.substring(1,3));
		if (month < 1 || month > 31) throw new MonthTooLarge(); // postcondition
	    } else {
		// ... fill in other format recognitions...
	    }
	}  
 	int firstSlash = date.indexOf('/');
 	if (firstSlash >=0) {
	    // this must be an English date; month is first
	    if (date.matches("\\d\\d/\\d\\d/\\d\\d\\d\\d")) {
		// Format: 10/21/2018
		month = Integer.parseInt(date.substring(0,2));
	    } else if (date.matches("\\d\\d/ \\d\\d/ \\d\\d\\d\\d")) {
		// Format: 10/ 21/ 2018
		month = Integer.parseInt(date.substring(0,2));
	    } else {
		// ... fill in other format recognitions...
	    }
	}  
	if (month > 31) throw new MonthTooLarge(); // postcondition
	if (month < 1) throw new DateWrong(date); // postcondition
	return month;
    }
    /**
       Parse a year. Similar algorithm, but year must be extracted from the end
       of the day string. 
    */ 
    public int parseYear(String date) throws DataFormatException {
 	int firstPoint = date.indexOf('.');
 	if (firstPoint >= 0) { // this must be a German date; year is last component
	    // cut off day
	    String monthYearString = date.substring(firstPoint+1); 
	    System.out.println("**test German year**:"+monthYearString);
	    // cut off month
	    int secondPoint = monthYearString.indexOf('.');
	    String yearString = monthYearString.substring(secondPoint+1); 
	    System.out.println("**test German year2**:"+yearString);
	    if (yearString.matches("\\d\\d\\d\\d")) {
		// Format: 2018
		year = Integer.parseInt(yearString.substring(0,4));
	    } else if (yearString.matches(" \\d\\d\\d\\d")) {
		// Format: ' 2018'
		year = Integer.parseInt(yearString.substring(1,5));
	    } else if (yearString.matches("\\d\\d")) {
		// Format: 18
		year = Integer.parseInt(yearString.substring(0,2));
	    } else {
		// ... fill in other format recognitions...
	    }
	    if (year < 1900 || year > 2050) throw new YearTooLarge(); // postcondition
	}  
 	int firstSlash = date.indexOf('/');
 	if (firstSlash >=0) {
	    // this must be an English date
	    // cut off day
	    String monthYearString = date.substring(firstSlash+1); 
	    System.out.println("**test English year**:"+monthYearString);
	    // cut off month
	    int secondSlash = monthYearString.indexOf('/');
	    String yearString = monthYearString.substring(secondSlash+1); 
	    System.out.println("**test English year2**:"+yearString);
	    if (yearString.matches("\\d\\d\\d\\d")) {
		// Format: 2018
		year = Integer.parseInt(yearString.substring(0,4));
	    } else if (yearString.matches("\\d\\d")) {
		// Format: 18
		year = Integer.parseInt(yearString.substring(0,2));
		year += 2000; // add 2000
	    } else {
		// ... fill in other format recognitions...
	    }
	    if (year < 1900 || year > 2050) throw new YearTooLarge(); // postcondition
	}  
	if (year < 1900 || year > 2050) throw new YearTooLarge(); // postcondition
	if (year < 1) throw new DateWrong(date); // postcondition
	return year;
    }
    /*
     * Parse a date string. Currently, only German and English numeric formats are
     * allowed.
     */
    public int parseDate (String date)  throws DataFormatException {
	try {
 	    day =  this.parseDay(date);
	    System.out.println("recognition status: Day/Month/Year:"+day+":"+month+":"+year);
	} catch (DataFormatException ex) {
	    System.err.println("Day is wrong: "+ex.toString());
	    return 0;
	}
	try {
 	    month =  this.parseMonth(date);
	} catch (DataFormatException ex) {
	    System.err.println("Month is wrong: "+ex.toString());
	    return 0;
	}
	    System.out.println("recognition status: Day/Month/Year:"+day+":"+month+":"+year);
	try {
 	    year =  this.parseYear(date);
	} catch (DataFormatException ex) {
	    System.err.println("Year is wrong: "+ex.toString());
	    return 0;
	}
	System.out.println("recognition status: Day/Month/Year:"+day+":"+month+":"+year);
	return day;
    }
}


/**
   DateInvalid is an Exception class. An Exception object is allocated by
   calling the constructor fof the superclass super(String s).
   A return statement is not necessary in a constructor.
*/
class DateInvalid extends DataFormatException {
    public DateInvalid() {
	super("The given date string is empty");
    }
};
class DateTooShort extends DataFormatException {
    public DateTooShort() {
	super("The given date string is too short");
    }
};
class DayTooLarge extends DataFormatException {
    public DayTooLarge() {
	super("The given day must be in the range of 1..31");
    }
};
class MonthTooLarge extends DataFormatException {
    public MonthTooLarge() {
	super("The given month must be in the range of 1..12");
    }
};
class YearTooLarge extends DataFormatException {
    public YearTooLarge() {
	super("The given year must be in the range of 1900..2050");
    }
};
class DateWrong extends DataFormatException {
    public DateWrong(String day) {
	super("Date format wrong: "+day);
    }
};
