001 package org.hackystat.utilities.time.interval; 002 003 import java.io.Serializable; 004 import java.text.DateFormatSymbols; 005 import java.text.ParseException; 006 import java.text.SimpleDateFormat; 007 import java.util.Comparator; 008 import java.util.Date; 009 import java.util.Locale; 010 import java.util.TreeMap; 011 012 import org.hackystat.utilities.time.period.Day; 013 import org.hackystat.utilities.time.period.Week; 014 015 /** 016 * Provides support for constants and methods in the interval selector. 017 * 018 * @author Hongbing Kou, Philip Johnson 019 */ 020 class IntervalUtility { 021 /** Year options from 2000 to 2020. */ 022 private TreeMap<String, String> yearOptions; //NOPMD 023 /** Three letters month map from Jan to Dec. */ 024 private TreeMap<String, String> monthOptions;//NOPMD 025 /** Day map from first day to last day of the month.*/ 026 private TreeMap<String, String> dayOptions; //NOPMD 027 /** Last half year's week map. */ 028 private TreeMap<String, String> weekOptions; //NOPMD 029 030 /** 031 * Initializes interval utility object. 032 */ 033 private IntervalUtility() { 034 // Day map 035 this.dayOptions = new TreeMap<String, String>(); 036 for (int i = 1; i <= 31; i++) { 037 String dayValue = (i < 10) ? "0" + i : String.valueOf(i); 038 this.dayOptions.put(dayValue, dayValue); 039 } 040 041 // Month options 042 this.monthOptions = new TreeMap<String, String>(); 043 DateFormatSymbols dateSymbols = new DateFormatSymbols(); 044 String[] monthStrings = dateSymbols.getMonths(); 045 046 for (int i = 0; i < monthStrings.length - 1; i++) { 047 String monthValue = (i >= 0 && i < 10) ? "0" + i : String.valueOf(i); 048 this.monthOptions.put(monthStrings[i], monthValue); 049 } 050 051 052 // Year options 053 this.yearOptions = new TreeMap<String, String>(); 054 for (int i = 2000; i < 2020; i++) { 055 String year = String.valueOf(i); 056 yearOptions.put(year, year); 057 } 058 059 // Week options 060 this.weekOptions = new TreeMap<String, String>(new WeekComparator()); 061 062 fillWeekOptions(); 063 } 064 065 /** 066 * Fills out the week options. 067 */ 068 private void fillWeekOptions() { 069 Day today = Day.getInstance(); 070 Week start = new Week(today.inc(-1 * 52 * 7)); 071 072 Week end = new Week(); 073 for (Week week = start; week.compareTo(end) <= 0; week = week.inc()) { 074 this.weekOptions.put(week.getWeekRepresentation(), week.getWeekRepresentation()); 075 } 076 } 077 078 /** 079 * Returns a Week instance given a week string. 080 * The week string will be something like "11-Jan-2004 to 17-Jan-2004". 081 * Test cases always pass an English locale-based string, so this method will 082 * first try to parse the string using the current locale, then try to parse 083 * it using Locale.US, then fail if neither works. 084 * 085 * @param weekString Week string from the week selector. 086 * @return Week object represented by the week string. 087 */ 088 public Week getWeek(String weekString) { 089 SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy", Locale.US); 090 Day day = null; 091 try { 092 day = Day.getInstance(formatter.parse(weekString.substring(0, 11))); 093 return new Week(day); 094 } 095 catch (ParseException e) { 096 // Now we're hosed; both the default and US Locales didn't work. 097 //ServerProperties.getInstance().getLogger().warning("Cannot parse the first day from " + 098 // "week string " + weekString + e); 099 throw new IllegalArgumentException("Week string " + weekString + " is not well formatted" + e); 100 } 101 } 102 103 /** 104 * Gets map of years. 105 * 106 * @return TreeMap of the year. 107 */ 108 public TreeMap<String, String> getYearOptions() { //NOPMD 109 return this.yearOptions; 110 } 111 112 /** 113 * Gets map of months. 114 * 115 * @return TreeMap of the months. 116 */ 117 public TreeMap<String, String> getMonthOptions() { //NOPMD 118 return this.monthOptions; 119 } 120 121 /** 122 * Gets map of weeks. 123 * 124 * @return TreeMap of the past 26 weeks. 125 */ 126 public TreeMap<String, String> getWeekOptions() { //NOPMD 127 checkWeekUpdates(); 128 return this.weekOptions; 129 } 130 131 /** 132 * Update the week options if today is not in. 133 */ 134 private void checkWeekUpdates() { 135 String lastWeekString = this.weekOptions.firstKey(); 136 Week endWeek = getWeek(lastWeekString); 137 138 // If today is not in the range of week then rebuild the week options. 139 Day today = Day.getInstance(); 140 if (today.compareTo(endWeek.getLastDay()) > 0) { 141 fillWeekOptions(); 142 } 143 } 144 145 /** 146 * Gets map of days. 147 * 148 * @return TreeMap of the days in a month. 149 */ 150 public TreeMap<String, String> getDayOptions() { //NOPMD 151 return this.dayOptions; 152 } 153 154 /** 155 * Gets the singleton interval utility class. 156 * 157 * @return Singleton interval utility object. 158 */ 159 public static IntervalUtility getInstance() { 160 return IntervalUtilityHolder.THE_INSTANCE; 161 } 162 163 /** Week comparator class. */ 164 private static class WeekComparator implements Comparator<String>, Serializable { 165 /** 166 * 167 */ 168 private static final long serialVersionUID = 1L; 169 170 /** 171 * Compares two month names. 172 * 173 * @param o1 The first month of the comparison 174 * @param o2 The second month of the comparison 175 * @return -1, 0, 1 if o1 is less than, equal to, or greater than o2 176 */ 177 public int compare(String o1, String o2) { 178 String week1 = o1; 179 String week2 = o2; 180 181 SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy", Locale.US); 182 183 try { 184 Date date1 = formatter.parse(week1.substring(0, 11)); 185 Date date2 = formatter.parse(week2.substring(0, 11)); 186 187 //return date1.compareTo(date2); 188 // Sort week in reverse chronological order. 189 return date2.compareTo(date1); 190 } 191 catch (ParseException e) { 192 System.out.println(o1 + " or " + o2 + " cannot be parsed for comparison"); 193 e.printStackTrace(); 194 return -1; 195 } 196 } 197 } 198 199 /** 200 * Creates a factory to hold the single IntervalUtility instance to avoid concurrent modification 201 * problem according to Page 194, Chapter 9 of Effective Java Programming. 202 */ 203 private static class IntervalUtilityHolder { 204 /** Singleton IntervalUtility object. */ 205 static final IntervalUtility THE_INSTANCE = new IntervalUtility(); //NOPMD 206 } 207 208 /** 209 * Get day object from year, month and day strings. 210 * 211 * @param yearString Year string in format '2004'. 212 * @param monthString Month string in format '00', '02', '20' etc. 213 * @param dayString Day string in '12', '02' etc. 214 * 215 * @return Day object. 216 */ 217 public Day getDay(String yearString, String monthString, String dayString) { 218 int year = Integer.parseInt(yearString); 219 int month = Integer.parseInt(monthString); 220 int day = Integer.parseInt(dayString); 221 222 return Day.getInstance(year, month, day); 223 } 224 225 /** 226 * Gets current year value in the year options. 227 * 228 * @return Current year string. 229 */ 230 public String getCurrentYear() { 231 return Day.getInstance().getYearString(); 232 } 233 234 /** 235 * Gets current month value. 236 * 237 * @return Current month string. 238 */ 239 public String getCurrentMonth() { 240 return Day.getInstance().getMonthString(); 241 } 242 243 /** 244 * Gets current day string. 245 * 246 * @return Current day string. 247 */ 248 public String getCurrentDay() { 249 return Day.getInstance().getDayString(); 250 } 251 252 /** 253 * Gets current week string. 254 * 255 * @return Current week 256 */ 257 public String getCurrentWeek() { 258 return this.weekOptions.lastKey(); 259 } 260 }