Reservation.java
1 |
|
2 |
import java.util.HashSet; |
3 |
import java.util.Date; |
4 |
|
5 |
/** |
6 |
* Represents a reservation in a hostel. |
7 |
* Reservation is a simple class that allows one to store reservation |
8 |
* information, request it when necessary, and so on. |
9 |
* Certain methods are provided for interaction, which use contracts to assert |
10 |
* proper functioning. |
11 |
* |
12 |
* Note: There is a form of redundancy in this class. |
13 |
* Reservation holds a Set of Beds, and an integer, indicating the amount of |
14 |
* people that are in this Reservation. |
15 |
* Normally, the amount of people in the Reservation is determined by the amount |
16 |
* of reserved Beds, because the amount of Beds implied the amount of people to |
17 |
* facilitate. |
18 |
* Because of the design of this program, I've weighed the advantages and |
19 |
* disadvantages of holding this implication, of using a seperate member |
20 |
* explicitely indicating the amount of people. |
21 |
* |
22 |
* I've chosen the latter option. It scales better in terms of weird |
23 |
* afterthoughts ("I also want to make people sleep on the floor"), but being |
24 |
* explicit is also a tad easier to manage. It also allows the other classes to |
25 |
* be far more decoupled from Reservation (otherwise, they'd have to check for |
26 |
* errors every time themselves, hindering cohesiveness). |
27 |
* |
28 |
* To overcome the possible difference, I've made it so that, every time, the |
29 |
* relation between the values is changed (the people in the Reservation |
30 |
* changes, for example), the class automatically checks if the values are |
31 |
* corresponding. If they're not, a warning is printed to system.out.err, |
32 |
* informing about the fact that there's a discrepancy. |
33 |
* |
34 |
* I think that's the best way to overcome this problem. |
35 |
* @author Maarten Vangeneugden - 1438256 |
36 |
*/ |
37 |
public class Reservation { |
38 |
|
39 |
// Members |
40 |
private String groupName; |
41 |
private Date beginDate; |
42 |
private Date endDate; |
43 |
private Set<Bed> reservedBeds; |
44 |
private int people; // The amount of people in this Reservation |
45 |
private int reservationID; |
46 |
private String roomType; // Room type requested by group |
47 |
private Set<String> roomFacilities; // Requested room facilities |
48 |
private Set<Date> breakfastDays; // Set of days numbers |
49 |
// Controllers |
50 |
private RoomController roomController; |
51 |
private ReservationController reservationController; |
52 |
|
53 |
/** |
54 |
* Create a new Reservation. |
55 |
* Be aware about the limitations of a Reservation: it's not the |
56 |
* Reservation's duty to check whether the provided Beds are properly |
57 |
* reserved; take care of this accordingly. |
58 |
* |
59 |
* Some information is implicit. For example: The size of the set of |
60 |
* reserved Beds implies the amount of people in the group; no breakfast |
61 |
* days implies no breakfast, ... |
62 |
* @param groupName The name of the group. |
63 |
* @param people The amount of people that are reserving. |
64 |
* @param beginDate The date that the Reservation begins. |
65 |
* @param endDate The date that the Reservation ends. |
66 |
* @param reservedBeds The set of Beds reserved and assigned to this |
67 |
* Reservation. |
68 |
* @param roomType The requested room type. |
69 |
* @param roomFacilities The set of all requested room facilities. |
70 |
* @param breakfastDays A set of all days that the group wants breakfast. |
71 |
* @param reservationID An ID for this reservation, to differentiate from |
72 |
* other Reservations. |
73 |
* @pre No parameter must be a null pointer. |
74 |
* @pre people must be a natural number, different from 0. |
75 |
* @pre beginDate must come before endDate. |
76 |
* @pre All dates in breakfastDays must fall between beginDate and endDate. |
77 |
* @pre No string parameter may be empty. |
78 |
* @post The amount of people in the Reservation is determined by the amount |
79 |
* of reserved Beds. |
80 |
* @throws NullPointerException if any parameter is a null pointer. |
81 |
* @throws IllegalArgumentException if any of the other preconditions is not |
82 |
* met. |
83 |
*/ |
84 |
public Reservation(String groupName, int people, Date beginDate, Date endDate, Set<Bed> reservedBeds, String roomType, Set<String> roomFacilities, Set<Date> breakfastDays) { |
85 |
// Contract validation: |
86 |
if(people < 1) { |
87 |
throw new IllegalArgumentException("The amount of people should be at least 1."); |
88 |
} |
89 |
if(!beginDate.before(endDate)) { |
90 |
throw new IllegalArgumentException("The begin date occurs after the end date."); |
91 |
} |
92 |
if(groupName.isEmpty() || roomType.isEmpty()) { |
93 |
throw new IllegalArgumentException("groupName and/or roomType were empty strings."); |
94 |
} |
95 |
for(String roomFacility: roomFacilities) { |
96 |
if(roomFacility.isEmpty()) { |
97 |
throw new IllegalArgumentException("One of the room facilities was an empty string."); |
98 |
} |
99 |
} |
100 |
for(Date breakfastDay : breakfastDays) { |
101 |
if(breakfastDay.before(beginDate) || breakfastDay.after(endDate)) { |
102 |
throw new IllegalArgumentException("One of the breakfast days occurs before/after the reservation period."); |
103 |
} |
104 |
} |
105 |
// Contract validated, execute constructor |
106 |
this.groupName = groupName; |
107 |
this.people = people; |
108 |
this.beginDate = beginDate; |
109 |
this.endDate = endDate; |
110 |
this.reservedBeds = reservedBeds; |
111 |
this.reservationID = reservationID; |
112 |
this.roomType = roomType; |
113 |
this.roomFacilities = roomFacilities; |
114 |
this.breakfastDays = breakfastDays; |
115 |
} |
116 |
|
117 |
/** |
118 |
* Construct template Reservation. |
119 |
* Use this constructor if you need to create an empty/new Reservation. |
120 |
* It omits the standard restrictions (for example, the group name will be |
121 |
* empty) to accomodate for the expected usage of this Reservation. |
122 |
*/ |
123 |
public Reservation() { |
124 |
this.groupName = "Blank name"; |
125 |
this.people = 1; |
126 |
this.beginDate = new Date(); |
127 |
this.endDate = new Date(); |
128 |
// Adding 1 day, because the end date must be after the begin date. |
129 |
this.endDate.setTime(this.beginDate.getTime() + (1000*60*60*24)); |
130 |
this.reservedBeds = new HashSet<>(); |
131 |
this.reservationID = 0; |
132 |
this.roomType = ""; |
133 |
this.roomFacilities = new HashSet<>(); |
134 |
this.breakfastDays = new HashSet<>(); |
135 |
} |
136 |
|
137 |
/** |
138 |
* Checks whether the amount of people corresponds with the reserved Beds. |
139 |
* Call this method whenever a change in the amount of Beds, or the amount |
140 |
* of people is made, or, whenever you need to assure consistency. |
141 |
* |
142 |
* It also prints a warning to system.out.err to inform about a discrepancy, |
143 |
* should one be found. |
144 |
* |
145 |
* @return True if the counts are consistent, false otherwise. |
146 |
*/ |
147 |
private boolean checkPeopleCountConsistency() { |
148 |
// The getters for people and beds mustn't be used, as this causes an |
149 |
// infinite recursion. |
150 |
int people = this.people; |
151 |
int beds = this.reservedBeds.size(); |
152 |
if(people != beds) { |
153 |
System.err.println("There's a discrepancy in the amount of people in Reservation "+ |
154 |
this.reservationID +":"); |
155 |
System.err.println("People: "+String.valueOf(people)); |
156 |
System.err.println("Reserved Beds: "+String.valueOf(beds)); |
157 |
return false; |
158 |
} |
159 |
return true; |
160 |
} |
161 |
|
162 |
/** |
163 |
* Set the group name for this Reservation. |
164 |
* @param groupName The new group name. |
165 |
* @pre groupName mustn't be empty, or a null pointer. |
166 |
* @post The group name is changed to the given name. |
167 |
* @throws NullPointerException if groupName is a null pointer. |
168 |
* @throws IllegalArgumentException if groupName is an empty String. |
169 |
*/ |
170 |
public void setGroupName(String groupName) { |
171 |
if(groupName.isEmpty()) |
172 |
throw new IllegalArgumentException("groupName is an empty String."); |
173 |
this.groupName = groupName; |
174 |
} |
175 |
|
176 |
/** |
177 |
* Retrieve the current group name. |
178 |
* @return The group name of this Reservation. |
179 |
*/ |
180 |
public String getGroupName() { |
181 |
return this.groupName; |
182 |
} |
183 |
|
184 |
/** |
185 |
* Get amount of people in this Reservation. |
186 |
* @post A warning will be printed to system.out.err if the amount of people |
187 |
* is inconsistent with the amount of reserved Beds. |
188 |
* @see Reservation.checkPeopleCountConsistency |
189 |
* @return The amount of people in this Reservation. |
190 |
*/ |
191 |
public int getPeople() { |
192 |
this.checkPeopleCountConsistency(); |
193 |
return this.people; |
194 |
} |
195 |
|
196 |
/** |
197 |
* Set the amount of people for this Reservation. |
198 |
* Note that this method will not notify you if the new amount of people is |
199 |
* equal to the previous amount. |
200 |
* |
201 |
* This method will print |
202 |
* @param people The new amount of people in this Reservation. |
203 |
* @pre people must be at least 1. |
204 |
* @post A warning will be printed to system.out.err if the amount of people |
205 |
* is inconsistent with the amount of reserved Beds. |
206 |
* @post The amount of people is changed to the given value. |
207 |
* @throws IllegalArgumentException if people is less than 1. |
208 |
*/ |
209 |
public void setPeople(int people) { |
210 |
if(people < 1) { |
211 |
throw new IllegalArgumentException("people must be at least 1."); |
212 |
} |
213 |
this.people = people; |
214 |
this.checkPeopleCountConsistency(); |
215 |
} |
216 |
|
217 |
/** |
218 |
* Returns a copy of the begin Date of this Reservation. |
219 |
* @return a copy of the begin Date of this Reservation. |
220 |
*/ |
221 |
public Date getBeginDate() { |
222 |
return (Date)this.beginDate.clone(); |
223 |
} |
224 |
|
225 |
/** |
226 |
* Set the begin date for this Reservation. |
227 |
* @param beginDate The new begin date. |
228 |
* @pre beginDate mustn't be a null pointer. |
229 |
* @pre beginDate must come strictly before the end date. |
230 |
* @post The begin date is updated accordingly. |
231 |
* @throws NullPointerException if beginDate is a null pointer. |
232 |
* @throws IllegalArgumentException if beginDate comes after the end date. |
233 |
*/ |
234 |
public void setBeginDate(Date beginDate) { |
235 |
if(beginDate.after(this.endDate)) |
236 |
throw new IllegalArgumentException("beginDate comes after the end date."); |
237 |
this.beginDate = beginDate; |
238 |
} |
239 |
|
240 |
/** |
241 |
* Returns a copy of the end Date of this Reservation. |
242 |
* @return a copy of the end Date of this Reservation. |
243 |
*/ |
244 |
public Date getEndDate() { |
245 |
return (Date)this.endDate.clone(); |
246 |
} |
247 |
|
248 |
/** |
249 |
* Set the end date for this Reservation. |
250 |
* @param endDate The new end date. |
251 |
* @pre endDate mustn't be a null pointer. |
252 |
* @pre endDate must come strictly after the begin date. |
253 |
* @post The end date is updated accordingly. |
254 |
* @throws NullPointerException if endDate is a null pointer. |
255 |
* @throws IllegalArgumentException if endDate comes after the begin date. |
256 |
*/ |
257 |
public void setEndDate(Date endDate) { |
258 |
if(endDate.before(this.beginDate)) |
259 |
throw new IllegalArgumentException("endDate comes before the begin date."); |
260 |
this.endDate = endDate; |
261 |
} |
262 |
|
263 |
public void setReservedBeds(Set<Bed> reservedBeds) { |
264 |
this.reservedBeds = reservedBeds; |
265 |
this.checkPeopleCountConsistency(); |
266 |
} |
267 |
|
268 |
public Set<Bed> getReservedBeds() { |
269 |
this.checkPeopleCountConsistency(); |
270 |
return reservedBeds; |
271 |
} |
272 |
|
273 |
// TODO: Write documentation for all of these, even though it's all mostly |
274 |
// copy/pasting. pfffff |
275 |
public void setReservationID(int reservationID) { |
276 |
this.reservationID = reservationID; |
277 |
} |
278 |
|
279 |
public int getReservationID() { |
280 |
return reservationID; |
281 |
} |
282 |
|
283 |
public void setRoomType(String roomType) { |
284 |
this.roomType = roomType; |
285 |
} |
286 |
|
287 |
public String getRoomType() { |
288 |
return roomType; |
289 |
} |
290 |
|
291 |
public void setRoomFacilities(Set<String> roomFacilities) { |
292 |
this.roomFacilities = roomFacilities; |
293 |
} |
294 |
|
295 |
public Set<String> getRoomFacilities() { |
296 |
return roomFacilities; |
297 |
} |
298 |
|
299 |
/** |
300 |
* Calculates the price of the Reservation, based on its current state. |
301 |
* Price table: |
302 |
* - 20/person/day |
303 |
* - 5 less in low season, 5 more in high season |
304 |
* - 4/breakfast ordered |
305 |
* - If room is fully booked by the group --> 10% discount |
306 |
* @param room The Room the Beds of this Reservation are in. |
307 |
* @pre The Room must contain all Beds of this Reservation. |
308 |
* @pre No parameter may be null. |
309 |
* @throws IllegalArgumentException if room doesn't contain all Beds. |
310 |
* @throws NullPointerException if any parameter is a null pointer. |
311 |
* @return The price in euros. |
312 |
*/ |
313 |
public int getPrice(Room room) { |
314 |
// Contract validation |
315 |
if(!room.getBeds().containsAll(this.reservedBeds)) { |
316 |
throw new IllegalArgumentException("room does not have all the Beds of this Reservation."); |
317 |
} |
318 |
// Contract validated |
319 |
int totalPrice = 0; |
320 |
// Jan - Apr: Mid |
321 |
// May - Aug: High |
322 |
// Sep - Dec: Low |
323 |
|
324 |
// Calculate bed prices |
325 |
int month = this.getBeginDate().getMonth(); |
326 |
int bedPrice = 20; |
327 |
if(month >=8) { // From September: |
328 |
bedPrice -= 5; |
329 |
} else if(month >=4) { // From May: |
330 |
bedPrice += 5; |
331 |
} |
332 |
// ΔDate represents the difference between the begin and end date. |
333 |
// allows to retrieve the days between them, because the difference is |
334 |
// expressed in milliseconds (nights = ΔDate ÷ 1000 ÷ 60 ÷ 60 ÷ 24) |
335 |
long deltaDate = this.getEndDate().getTime() - this.getBeginDate().getTime(); |
336 |
long nights = deltaDate/1000/60/60/24; |
337 |
totalPrice += (this.getReservedBeds().size() * nights * bedPrice); |
338 |
// Calculate price for breakfasts |
339 |
int breakfasts = this.getBreakfastDays().size(); |
340 |
totalPrice += breakfasts * this.getPeople(); |
341 |
// Check if eligible for discount; |
342 |
// A group gets 10% discount iff they reserve a full Room for |
343 |
// themselves. |
344 |
Set<Bed> roomBeds = room.getBeds(); |
345 |
if(roomBeds.containsAll(this.reservedBeds) && roomBeds.size() == this.reservedBeds.size()) { |
346 |
double discount = (double)totalPrice * 0.1; |
347 |
totalPrice -= (int)discount; |
348 |
} |
349 |
return totalPrice; |
350 |
} |
351 |
|
352 |
public void setBreakfastDays(Set<Date> breakfastDays) { |
353 |
this.breakfastDays = breakfastDays; |
354 |
} |
355 |
public Set<Date> getBreakfastDays() { |
356 |
return this.breakfastDays; |
357 |
} |
358 |
|
359 |
} |
360 |