001    package org.hackystat.utilities.time.period;
002    
003    import java.text.SimpleDateFormat;
004    import java.util.ArrayList;
005    import java.util.Calendar;
006    import java.util.List;
007    import java.util.Locale;
008    
009    import javax.xml.datatype.XMLGregorianCalendar;
010    
011    /**
012     * Provides the Month abstract data type, which represents the collection of Week and Day
013     * instances in a given month.
014     * 
015     * The Calendar is forced to Locale.US to ensure constant week boundaries. 
016     * 
017     * @author Hongbing Kou
018     */
019    public class Month implements TimePeriod {
020      /** Calendar year. */
021      private int year;
022      /** Calendar month. */
023      private int month;
024      /** Number of days in this month. */
025      private int numOfDays;
026      /** First day of the month. */
027      private Day firstDay;
028      /** Last day of the month. */
029      private Day lastDay;
030      /** Days' list in a month. */
031      private List<Day> days;  
032      
033      /**
034       * Creates a Month instance for the given year and month.
035       * 
036       * @param year The calendar year, such as 2004.
037       * @param month The zero-based calendar month, such as 0 (January) or 11 (December).
038       */
039      public Month(int year, int month) {
040        this.year = year;
041        this.month = month;
042        
043        Calendar cal = Calendar.getInstance(Locale.US);
044        cal.set(Calendar.DAY_OF_MONTH, 1);
045        cal.set(Calendar.YEAR, this.year);
046        cal.set(Calendar.MONTH, this.month);
047    
048        this.numOfDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
049        this.firstDay = Day.getInstance(year, month, 1);    
050        this.lastDay = Day.getInstance(year, month, this.numOfDays);
051        
052        this.days = new ArrayList<Day>();
053        for (int i = 0; i < this.numOfDays; i++) {
054          this.days.add(Day.getInstance(year, month, i));
055        }
056      }
057      
058      /**
059       * Returns the Month instance corresponding to the passed XMLGregorianCalendar instance. 
060       * @param xmlDay The XMLGregorianCalendar date. 
061       */
062      public Month(XMLGregorianCalendar xmlDay) {
063        this(xmlDay.getYear(), xmlDay.getMonth() - 1);
064      }
065      
066      /**
067       * Returns the year associated with this Month.
068       * 
069       * @return The year, such as 2004.
070       */
071      public int getYear() {
072        return this.year;
073      }
074      
075      /**
076       * Returns the zero-based month associated with this Month.
077       * 
078       * @return The month, such as 0 (January) or 11 (December).
079       */
080      public int getMonth() {
081        return this.month;
082      }
083     
084      /**
085       * Returns the first Day instance associated with this Month.
086       * 
087       * @return First Day of this Month.
088       */
089      public Day getFirstDay() {
090        return this.firstDay;
091      }
092    
093      /**
094       * Returns the Week instance associated with the first day in this Month.
095       * Note that the Week instance may include Day instances from the prior Month.
096       * 
097       * @return The Week instance that includes the first Day of this Month.
098       */
099      public Week getFirstWeekInMonth() {
100        return new Week(this.firstDay);
101      }
102        
103      /**
104       * Returns the last Day instance associated with this Month.
105       * 
106       * @return Last day of the month.
107       */
108      public Day getLastDay() {
109        return this.lastDay;
110      }
111    
112      /**
113       * Returns the Week instance associated with the last Day of this Month. 
114       * Note that the Week instance may include Day instances from the next Month.
115       * 
116       * @return Last week in the month.
117       */
118      public Week getLastWeekInMonth() {
119        return new Week(this.lastDay);
120      }
121    
122      /**
123       * Returns the number of days in this Month.
124       * 
125       * @return Days in this month.
126       */
127      public int getNumOfDays() {
128        return this.numOfDays;    
129      }
130      
131      /**
132       * Compares two Month instances.
133       * 
134       * @param o Another month object.
135       * @return Comparison result.
136       */
137      public int compareTo(Object o) {
138        Month month = (Month) o;
139        
140        if (this.year == month.year) { 
141          return this.month - month.month; 
142        }
143        else {
144          return this.year - month.year;
145        }
146      }
147    
148      /**
149       * Returns the Month instance prior to this Month.
150       * 
151       * @return Previous month.
152       */
153      public Month dec() {
154        if (this.month == 0) {
155          return new Month(this.year - 1, 11);    
156        }
157        else {
158          return new Month(this.year, this.month - 1);
159        }     
160      }
161       
162      /**
163       * Returns the Month instance after this Month.
164       * 
165       * @return Next month.
166       */
167      public Month inc() {
168        if (this.month == 11) {
169          return new Month(this.year + 1, 0);    
170        }
171        else {
172          return new Month(this.year, this.month + 1);
173        }     
174      }
175      
176      /**
177       * Hash code of this Month.
178       * 
179       * @return Month's hashcode.
180       */
181      @Override
182      public int hashCode() {
183        String monthString = "" + this.year + "" + this.month;
184        return monthString.hashCode();
185      }
186      
187      /**
188       * Returns true if the passed object is a month and is equal to this Month.
189       * 
190       * @param obj Any object.
191       * @return True if two months are the equal.
192       */
193      @Override
194      public boolean equals(Object obj) {
195        return ((obj instanceof Month) && 
196                (this.year == ((Month) obj).year && this.month == ((Month) obj).month));  
197      }
198      
199      /**
200       * String representation of this Month.
201       * 
202       * @return Month string.
203       */
204      @Override
205      public String toString() {
206        SimpleDateFormat formatter = new SimpleDateFormat("MMM-yyyy", Locale.US);
207        Day day = Day.getInstance(this.year, this.month, 1);
208        return formatter.format(day.getDate());
209      }
210    
211      /**
212       * Returns a list of Day instances from the first Day to the last Day in this Month.
213       * 
214       * @return A sorted list of the Days in this Month.
215       */
216      public List<Day> getDays() {
217        return this.days;
218      }
219      
220      
221    }