OOP2

Reservation.java

1
import java.util.Set;
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