OOP2

Major refactor of code + add info about design choices

Author
Vngngdn
Date
Dec. 16, 2016, 2:38 a.m.
Hash
3c3f7b0921297dda9295fdf1db792c697d92e189
Parent
f68e848416b19091f2f20166cbd19ebec9fa4bc3
Modified files
Challenge 6/Bed.java
Challenge 6/Reservation.java
Challenge 6/ReservationController.java
Challenge 6/ReservationView.java
Challenge 6/Room.java
Challenge 6/RoomController.java
Challenge 6/Window.java
Challenge 6/ontwerpkeuzes2.md

Challenge 6/Bed.java

11 additions and 10 deletions.

View changes Hide changes
1
1
import java.util.Map;
2
2
import java.util.HashMap;
3
3
4
4
/**
+
5
/**
5
6
 * Class representing a bed in the hostel.
6
7
 * Even though beds in a hostel could simply be an integer in a Room, these Beds
7
8
 * need to remember on what dates they are reserved.
8
9
 * @author Maarten Vangeneugden - 1438256
9
10
 */
10
11
public class Bed {
11
12
12
13
	/* Rationale:
13
14
	 * I know that a mapping is not necessarily the best solution for storing
14
15
	 * periods, because it doesn't offer built-in protection yadda yadda.
15
16
	 * However, that's Java's fault, as it doesn't provide a Pair<L,R> thing in
16
17
	 * the STL.
17
18
	 * The best solution is actually a collection of tuples: (Begin, End), but
18
19
	 * that doesn't exist in Java, and thus requires a custom implementation.
19
20
	 * All things considered, a Map is the least bad choice.
20
21
	 */
21
22
	private Map<Date, Date> reservedPeriods;
22
23
23
24
	public Bed() {
24
25
		this.reservedPeriods = new HashMap<>();
25
26
	}
26
27
27
28
	/**
28
29
	 * Reserves this Bed for the given period.
29
30
	 * This method will mark the given period for this Bed as "reserved", which
30
31
	 * can then be cancelled or queried later.
31
32
	 * @param begin The time where the reservation begins.
32
33
	 * @param end The time where the reservation ends.
33
34
	 * @pre No parameter may be a null pointer.
34
35
	 * @pre The given period mustn't overlap with a period already marked as
35
36
	 * reserved.
36
37
	 * @pre begin must come before end.
37
38
	 * @pre begin and end mustn't be equal.
38
39
	 * @post The given period will be marked as "Reserved", and will have to be
39
40
	 * manually cancelled/removed in order to reserve again.
40
41
	 * @throws NullPointerException if any parameter is a null pointer.
41
42
	 * @throws IllegalArgumentException if begin is equal to, or comes after
42
43
	 * end.
43
44
	 * @throws DateTimeException if the given period overlaps with an already
44
45
	 * reserved period.
45
46
	 */
46
47
	public void reservePeriod(Date begin, Date end) {
47
48
		if(!begin.before(end)) {
48
49
			throw IllegalArgumentException("The begin date occurs after the end date.");
49
-
		}
+
50
		}
50
51
		if(!this.isFree(begin, end)) {
51
52
			throw DateTimeException("This period overlaps with a reserved period.");
52
-
		}
+
53
		}
53
54
		// Contract validated, execute method
54
55
		this.reservedPeriods.put(begin, end);
55
56
	}
56
57
57
58
	/**
58
59
	 * Remove a previous registration from this Bed.
59
60
	 * This method will remove/cancel the given reservation period from the
60
61
	 * Bed, opening it up for reservation again.
61
62
	 * @param begin The time where the reservation begins.
62
63
	 * @param end The time where the reservation ends.
63
64
	 * @pre No parameter may be a null pointer.
64
65
	 * @pre The given period must already be marked as "Reserved" in this Bed.
65
66
	 * @pre begin must come before end.
66
67
	 * @pre begin and end mustn't be equal.
67
68
	 * @post The given period will lose its "Reserved" mark, allowing the period
68
69
	 * to be reserved again.
69
70
	 * @throws NullPointerException if any parameter is a null pointer.
70
71
	 * @throws IllegalArgumentException if begin is equal to, or comes after
71
72
	 * end; or, if the given period is not reserved.
72
73
	 */
73
74
	public void removeReservationPeriod(Date begin, Date end) {
74
75
		if(!begin.before(end)) {
75
76
			throw IllegalArgumentException("The begin date occurs after the end date.");
76
-
		}
+
77
		}
77
78
		if(!this.isFree(begin, end)) {
78
79
			throw DateTimeException("This period overlaps with a reserved period.");
79
-
		}
+
80
		}
80
81
		// Contract partially validated; further validation occurs while looking
81
82
		// for the reservation.
82
83
		// XXX: Check if Java correctly handles equality: 2 different Date
83
84
		// objects with the same date representation should be equal!
84
85
		boolean reservationFound = this.reservedPeriods.remove(begin, end);
85
86
		if(!reservationFound) {
86
87
			throw IllegalArgumentException("The given period was not marked as reserved.");
87
-
		}
+
88
		}
88
89
	}
89
90
		
90
91
	/**
91
92
	 * Checks whether this Bed can be reserved in the given period.
92
93
	 * Use this method whenever you need to inform yourself about any
93
94
	 * conflicting reservation period.
94
95
	 * @param begin The time where the reservation begins.
95
96
	 * @param end The time where the reservation ends.
96
97
	 * @pre No parameter must be a null pointer.
97
98
	 * @pre begin must come before end.
98
99
	 * @pre begin and end mustn't be equal.
99
100
	 * @throws NullPointerException if any parameter is a null pointer.
100
101
	 * @throws IllegalArgumentException if begin is equal to, or comes after
101
102
	 * end.
102
103
	 * @return True if the given period does not overlap with any reservation, false otherwise.
103
104
	 */
104
105
	public boolean isFree(Date begin, Date end) {
105
106
		if(!begin.before(end)) {
106
107
			throw IllegalArgumentException("The begin date occurs after the end date.");
107
-
		}
+
108
		}
108
109
		if(!this.isFree(begin, end)) {
109
110
			throw DateTimeException("This period overlaps with a reserved period.");
110
-
		}
+
111
		}
111
112
		// Contract validated, execute method
112
113
		for(Map.Entry<Date, Date> reservedPeriod: this.reservedPeriods.EntrySet()) {
113
-
			Date reservedBegin = reservedPeriod.key();
114
-
			Date reservedEnd = reservedPeriod.value();
115
-
			/* Forbidden possibilities:
+
114
			Date reservedBegin = reservedPeriod.getKey();
+
115
			Date reservedEnd = reservedPeriod.getValue();
+
116
			/* Forbidden possibilities:
116
117
			 * (A,B = reserved; X,Y = requested)
117
118
			 * X-A-Y-B / A-X-B-Y  -  Begins or ends in a reserved period
118
119
			 * X-A-B-Y  -  Complete overlapping of reserved period
119
120
			 * Allowed possibilities:
120
121
			 * A-B-X-Y / X-Y-A-B  -  No overlapping
121
122
			 */
122
123
			if((begin.after(reservedBegin) && begin.before(reservedEnd)) ||
123
124
			   (end.after(reservedBegin) && end.before(reservedEnd))) {
124
125
				// Triggered if any forbidden structure is detected
125
126
				return false;
126
127
			}
127
128
		}
128
129
		return true; // No overlapping found
129
130
	}
130
131
131
132
	/**
132
133
	 * Checks whether this Bed has open reservation periods.
133
134
	 * @return True if this Bed has reserved periods, false otherwise.
134
135
	 */
135
136
	public boolean hasReservations() {
136
137
		return !this.reservedPeriods.isEmpty();
137
138
	}
138
139
}
139
140

Challenge 6/Reservation.java

63 additions and 41 deletions.

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

Challenge 6/ReservationController.java

10 additions and 8 deletions.

View changes Hide changes
1
1
import java.util.HashSet;
2
2
import java.util.Date;
3
3
4
4
/**
5
5
 * Controller class for the Reservations of this program.
6
6
 * Since this program handles a youth hostel, it's imperative that it holds a
7
7
 * number of Reservations.
8
8
 * 
9
9
 * ReservationController takes the responsibility of handling the addition,
10
10
 * cancelling, editing, ... of Reservations, and the related tasks inherent to
11
11
 * these responsibilities, such as reserving beds, generating IDs, ...
12
12
 * @author Maarten Vangeneugden - 1438256
13
13
 */
14
14
public class ReservationController {
15
15
16
16
	/* Rationale:
17
17
	 * Normally I'd put this as an interface (Set), but an interface does not
18
18
	 * inherit from Object, and thus, does not provide clone(). This is
19
19
	 * necessary, because otherwise, there's no point in having private
20
20
	 * members.
21
21
	 */
22
22
	private HashSet<Reservation> reservations; // Holds all Reservations
23
23
	private RoomController roomController;
24
24
25
25
	/**
26
26
	 * Creates the ReservationController.
27
27
	 * 
28
28
	 * WARNING: This program avoids the use of null pointers, but because of
29
29
	 * circular dependency issues, this class requires that the developer (which
30
30
	 * means YOU) set the RoomController reference member manually.
31
31
	 * Be advised that the setRoomController() method will not accept a null
32
32
	 * pointer.
33
33
	 *
34
34
	 * Once again: Set the RoomController reference manually ASAP. Failure to do
35
35
	 * so will cause this program to blow up. You've been warned.
36
36
	 * @see ReservationController.setRoomController
37
37
	 */
38
38
	public ReservationController() {
39
39
		this.reservations = new HashSet<>();
40
40
	}
41
41
42
42
	/**
43
43
	 * Returns a copy of all Reservations.
44
44
	 * Emphasis on "copy"; There is no setReservations() for a reason, using
45
45
	 * this to edit the pointer variable would omit the use of 'private'.
46
46
	 * @return A verbatim copy of all Reservations.
47
47
	 */
48
48
	@SuppressWarnings("unchecked")
49
49
	public Set<Reservation> getReservations() {
50
50
		return (HashSet<Reservation>)this.reservations.clone();
51
51
	}
52
52
53
53
	/**
54
54
	 * Generates a unique ID for a new Reservation.
55
55
	 * This method is to be called when a new Reservation is to be stored.
56
56
	 * As every Reservation carries an ID, this method searches for a unique
57
57
	 * one, and returns it.
58
58
	 * @return An integer, different from all other stored Reservation IDs.
59
59
	 */
60
60
	public int generateReservationID() {
61
61
		/* Small optimization idea:
62
62
		 * Instead of starting from 0, and incrementing until it's unique, it
63
63
		 * will take the amount of stored Reservations, and test that as an ID.
64
64
		 * This may still overlap (by removing an older Reservation), but may be
65
65
		 * much faster. Consider implemting and testing for effectiveness if
66
66
		 * generating an ID takes too long.
67
67
		 */
68
68
		int ID = 0;
69
69
		boolean isUnique = false;
70
70
		do {
71
71
			ID++;
72
72
			isUnique = true;
73
73
			for(Reservation reservation: this.reservations) {
74
74
				if(ID == reservation.getReservationID()) {
75
75
					isUnique = false;
76
76
				}
77
77
			}
78
78
		} while(!isUnique);
79
79
		// Test:
80
80
		for(Reservation reservation: this.reservations) {
81
81
			assert reservation.getReservationID() != ID : "Duplicate ID generated!";
82
82
		}
83
83
		return ID;
84
84
	}
85
85
	
86
86
	/** 
87
87
	 * Check if Reservation can be made.
88
88
	 * Call this method whenever you're planning on adding a new Reservation. It
89
89
	 * will check the provided requirements (Room facilities, for example), and
90
90
	 * return whether this Reservation can continue.
91
91
	 * @param reservation The Reservation to check for possible addition.
92
92
	 * @pre reservation mustn't be null.
93
93
	 * @throws NullPointerException if reservation is null.
94
94
	 * @return True if reservation can be added, False otherwise.
95
95
	 */
96
96
	public boolean checkReservation(Reservation reservation) {
97
97
		if(reservation == null) {
98
98
			throw NullPointerException("reservation was a null pointer.");
99
-
		}
+
99
		}
100
100
		if(this.getRoomController().getQualifiedRooms(reservation).size() == 0) {
101
-
			return null;
102
-
		}
+
101
			return false;
+
102
		}
103
103
	}
+
104
		return true;
+
105
	}
104
106
105
107
	/**
106
108
	 * Adds and confirms the reservation.
107
109
	 * By calling this method, the given reservation will be stored in the
108
110
	 * system, and the given room will be filled.
109
111
	 * You are to collect the necessary data, and assure yourself that all
110
112
	 * preconditions have been met.
111
113
	 * 
112
114
	 * The best way to accomplish this, is to only send 'sterile' Reservations
113
115
	 * as the first parameter. That is: Reservations without reserved Beds,
114
116
	 * without prior addition to the active Reservations, etc.
115
117
	 * NOTE: You must check for yourself if the Reservation can take place in
116
118
	 * this Room! If there are not enough Beds available, an exception will be
117
119
	 * thrown.
118
120
	 * @param reservation The Reservation to add.
119
121
	 * @param room The Room in which the people will reside.
120
122
	 * @pre room must have enough empty beds.
121
123
	 * @pre room must accomodate the Reservation's requirements.
122
124
	 * @pre No parameter must be null.
123
125
	 * @pre reservation mustn't already be stored in the active Reservations.
124
126
	 * @post reservation is stored in the active Reservations.
125
127
	 * @post The Beds in the provided Room are reserved for the Reservation's
126
128
	 * period.
127
129
	 * @throws IllegalArgumentException if the given Room can't fulfill the
128
130
	 * requirements of the Reservation, or occupants is less than 1.
129
131
	 * @throws NullPointerException if any parameter is a null pointer.
130
132
	 */
131
133
	public void addReservation(Reservation reservation, Room room) {
132
134
		// Contract validation. Null pointers are implicitely checked by calling
133
135
		// methods.
134
136
		if(reservation.getPeople() > room.getEmptyBeds(reservation.getBegin(), reservation.getEnd()).size())
135
137
			throw IllegalArgumentException("The given Room has not enough empty Beds for the Reservation period.");
136
-
		if(!this.getRoomController().getQualifiedRooms(reservation).contains(room))
+
138
		if(!this.getRoomController().getQualifiedRooms(reservation).contains(room))
137
139
			throw IllegalArgumentException("The given Room cannot meet all requirements of the Reservation.");
138
-
		if(this.getReservations().contains(reservation))
+
140
		if(this.getReservations().contains(reservation))
139
141
			throw IllegalArgumentException("The given Reservation was already included in the active Reservations.");
140
-
		// Contract validated
+
142
		// Contract validated
141
143
		this.reservations.add(reservation);
142
144
		Bed[] beds = room.getEmptyBeds(reservation.getBegin(), reservation.getEnd()).toArray(new Bed[1]);
143
145
		Set<Bed> reservedBeds = new HashSet<>();
144
146
		for(int i=0; i<reservation.getPeople(); i++) {
145
147
			beds[i].reservePeriod(reservation.getBegin(), reservation.getEnd());
146
148
			reservedBeds.add(beds[i]);
147
149
		}
148
150
		reservation.setReservedBeds(reservedBeds);
149
151
	}
150
152
151
153
	/**
152
154
	 * Cancels and removes the given Reservation.
153
155
	 * If you want to remove a Reservation, use this method, and provide the
154
156
	 * Reservation up for removal.
155
157
	 * This method will take care of related actions, such as releasing Beds.
156
158
	 * @param reservation The Reservation to be removed.
157
159
	 * @pre reservation mustn't be null.
158
160
	 * @pre reservation must be contained in the active Reservations.
159
161
	 * @post The Reservation is removed from the active Reservations.
160
162
	 * @post The Beds, previously reserved for this Reservation, are now
161
163
	 * released, and can be reserved for another Reservation.
162
164
	 * @throws NullPointerException if reservation is a null pointer.
163
165
	 * @throws IllegalArgumentException if reservation is not contained in the
164
166
	 * active Reservations.
165
167
	 */
166
168
	public void cancelReservation(Reservation reservation) {
167
169
		// Contract validation
168
170
		if(!this.getReservations().contains(reservation)) {
169
171
			throw IllegalArgumentException("The given Reservation was not contained in the active Reservations.");
170
-
		}
+
172
		}
171
173
		if(reservation == null) {
172
174
			throw NullPointerException();
173
-
		}
+
175
		}
174
176
		// Contract validated, execute method
175
177
		this.reservations.remove(reservation); // Remove from active Reservations
176
178
		for(Bed bed: reservation.getReservedBeds()) { // Release reserved Beds
177
179
			bed.removeReservationPeriod(reservation.getBegin(), reservation.getEnd());
178
180
		}
179
181
		// Asserting post conditions are met
180
182
		assert !this.getReservation().contains(reservation) : "The reservation is still part of the active Reservations.";
181
183
		for(Bed bed: reservation.getReservedBeds()) {
182
184
			assert bed.isFree(reservation.getBegin(), reservation.getEnd()) : "One or more of the Beds are still reserved.";
183
185
		}
184
186
	}
185
187
186
188
}
187
189

Challenge 6/ReservationView.java

7 additions and 5 deletions.

View changes Hide changes
1
1
import java.util.Set;
2
2
import java.util.HashSet;
3
3
import java.util.Date;
4
4
/**
+
5
/**
5
6
 * Creates a view screen that allows interaction with the reservation.
6
7
 * This window will place the reservation details in editable fields, so its
7
8
 * state can be adapted.
8
9
 * It then also adds a couple of buttons; add/update/cancel, with their
9
10
 * respective actions. These will then be passed to the ReservationController.
10
11
 * It will not allow to add/update a reservation if dates overlap, i.e. beds
11
12
 * can't be reserved for that period.
12
13
 * @author Maarten Vangeneugden - 1438256
13
14
 */
14
15
public class ReservationView {
15
16
16
17
	private Reservation reservation;
17
18
	private Window window;
18
19
19
20
	// Controllers for communication with the rest
20
21
	private ReservationController rc;
21
22
	private RoomController roc;
22
23
23
24
	// GUI widgets
24
25
	private JTextField nameField;
25
26
	private JSpinner amountPeopleField;
26
27
	private JTextField dateField;
27
-
	private JSpinner durationField;
+
28
	private JTextField endDateField;
+
29
	private JSpinner durationField;
28
30
	private JRadioButton[] typeField;
29
31
	private JCheckBox showerField;
30
32
	private JCheckBox bathField;
31
33
	private JCheckBox minibarField;
32
34
	private JCheckBox aircoField;
33
35
34
36
35
37
	/**
36
38
	 * Construct a window to edit a Reservation.
37
39
	 * This window presents its caller with the necessary tools to edit the
38
40
	 * given Reservation, and save it in the system.
39
41
	 *
40
42
	 * By sending it an empty Reservation, you can add a new Reservation to the
41
43
	 * system.
42
44
	 * This class is built in a way that allows it to be used for both creating
43
45
	 * new Reservations, and updating/changing existing Reservations.
44
46
	 * Existing Reservations can also be removed by clicking the appropriate
45
47
	 * button.
46
48
	 * @param reservation The Reservation that will be updated.
47
49
	 * @param rc The ReservationController class.
48
50
	 * @param roc The RoomController class.
49
51
	 * @pre No parameter may be a null pointer.
50
52
	 * @post A window is displayed with GUI widgets, which are filled in
51
53
	 * according to the state of reservation.
52
54
	 * @throws NullPointerException if any parameter is a null pointer.
53
55
	 */
54
56
	public ReservationView(Reservation reservation, ReservationController rc, RoomController roc) {
55
57
		// Contract validation
56
58
		if(rc == null || roc == null || reservation == null)
57
59
			throw NullPointerException("One or more of the given parameters is a null pointer.");
58
-
		// Contract validated
+
60
		// Contract validated
59
61
		this.rc = rc;
60
62
		this.roc = roc;
61
63
62
64
		this.reservation = reservation;
63
65
		this.window = new Window("Reservation screen");
64
66
		this.addFields();
65
67
	}
66
68
67
69
	public void setReservation(Reservation reservation) {
68
70
		this.reservation = reservation;
69
71
	}
70
72
71
73
	public Reservation getReservation() {
72
74
		return reservation;
73
75
	}
74
76
75
77
	/**
76
78
	 * Add the necessary fields to the window.
77
79
	 * This method inspects the Reservation, and copies the included information
78
80
	 * to the widget's content.
79
81
	 */
80
82
	private void addFields() {
81
83
		this.window.createLabel("Group name");
82
84
		this.nameField = window.createTextField(this.reservation.getGroupName());
83
85
		this.window.createLabel("Amount of people");
84
86
		this.amountPeopleField = window.createSpinner(1, 20, this.reservation.getPeople(), 1);
85
87
		// Formatting date for the date field:
86
88
		this.window.createLabel("Begin date");
87
89
		this.beginDateField = window.createTextField(this.reservation.getBegin().toString());
88
90
		this.window.createLabel("End date");
89
91
		this.endDateField = window.createTextField(this.reservation.getEnd().toString());
90
92
		String[] types = {"Male", "Female", "Mixed"};
91
93
		this.typeField = this.window.createRadioButtons(types);
92
94
		// TODO: Add a test to see if the Reservation has indicated which type
93
95
		// it is, and then already check the corresponding radio button.
94
96
		this.showerField = this.window.createCheckbox("Shower");
95
97
		this.bathField = this.window.createCheckbox("Bath");
96
98
		this.minibarField = this.window.createCheckbox("Minibar");
97
99
		this.aircoField = this.window.createCheckbox("Airco");
98
100
		// TODO: Idem for the facilities, test if already in Reservation, and
99
101
		// check accordingly.
100
102
		this.window.createButton("Add/Update reservation", "", "addReservation", this);
101
103
		this.window.createButton("Remove reservation", "", "removeReservation", this);
102
104
	}
103
105
104
106
	/**
105
107
	 * Start the addition of this Reservation to the system.
106
108
	 * This method will check the current fields, and update the Reservation
107
109
	 * with those values. In doing so, the validity of the values is checked
108
110
	 * (formatting, begin date before end date, ...).
109
111
	 * If everything is okay, the method will check if these changes are
110
112
	 * possible in the current system; are there enough empty beds?
111
113
	 *
112
114
	 * If everything is okay, the changes will be propagated to the
113
115
	 * Reservation's state, and (if it's a new Reservation) will be added to the
114
116
	 * system's active Reservations.
115
117
	 */
116
118
	public void addReservation() {
117
119
		// Collect all data from the fields
118
120
		String name = this.nameField.getText();
119
121
		Date beginDate = null, endDate = null;
120
122
		try {
121
123
			beginDate = DataFormat.parse(this.beginDateField.getText());
122
-
			endDate = DataFormat.parse(this.endDateField.getText());
123
-
		}
+
124
		endDate = new Date(this.endDateField.getText());
+
125
		}
124
126
		catch(ParseException e) {
125
-
			this.window.messageDialog("Not all date fields were properly formatted.");
+
127
			this.window.messageDialog("Not all date fields were properly formatted.");
126
128
			return;
127
129
		}
128
130
		int people = (Integer)this.amountPeopleField.getValue();
129
131
		String type = "";
130
132
		for(int i=0; i<this.typeField.length; i++) {
131
133
			if(this.typeField[i].isSelected()) {
132
134
				type = this.typeField[i].getText();
133
135
				break;
134
136
			}
135
137
		}
136
138
		Set<String> facilities = new HashSet<>();
137
139
		if(this.showerField.isSelected()) {
138
140
			facilities.add("Shower");
139
141
		}
140
142
		if(this.bathField.isSelected()) {
141
143
			facilities.add("Bath");
142
144
		}
143
145
		if(this.minibarField.isSelected()) {
144
146
			facilities.add("Minibar");
145
147
		}
146
148
		if(this.aircoField.isSelected()) {
147
149
			facilities.add("Airco");
148
150
		}
149
151
150
152
		// TODO breakfast days handling
151
153
152
154
		//Set<Room> possibleRooms = this.roc.getQualifiedRooms(actualDate, duration, type, facilities);
153
155
		// TODO: Refactor upper method to work with the Reservation.
154
156
		// TODO: Implement all checks of valid data above this line!
155
157
		
156
158
		// Data validated; Try finding appropriate beds
157
159
		if(possibleRooms.size() == 0) {
158
160
			boolean tryAgain = this.window.confirmDialog("No rooms met the requirements! Would you like to continue and change the parameters?");
159
161
			if(!tryAgain) {
160
162
				// TODO close window
161
163
			}
162
164
		}
163
165
		else {
164
166
			Room pickedRoom = null;
165
167
			for(Room room: possibleRooms) {
166
168
				if(room.getEmptyBeds(actualDate, actualEndDate).size() < room.getBeds().size()) {
167
169
					// First, fill the rooms that are partially filled.
168
170
					pickedRoom = room;
169
171
					break;
170
172
				}
171
173
			}
172
174
			if(pickedRoom == null) { // If still no room, pick an empty room
173
175
				for(Room room: possibleRooms) {
174
176
					if(room.getEmptyBeds(actualDate, actualEndDate).size() >= amount) {
175
177
						pickedRoom = room;
176
178
						break;
177
179
					}
178
180
				}
179
181
			}
180
182
			assert pickedRoom != null;
181
183
			// TODO: Set reservation fields here!
182
184
			this.rc.addReservation(reservation, pickedRoom);
183
185
184
186
			// Confirm and show price:
185
187
			int price = this.reservation.getPrice();
186
188
			this.window.confirmDialog("Reservation confirmed! Price: " +String.valueOf(price));
187
189
		}
188
190
		
189
191
	}
190
192
191
193
	/**
192
194
	 * Remove Reservation and the GUI.
193
195
	 * Calling this method will remove the Reservation, followed by closing this
194
196
	 * window.
195
197
	 * If the Reservation is active (i.e. saved in the ReservationController),
196
198
	 * it will be removed.
197
199
	 * Any changes will be cancelled.
198
200
	 */
199
201
	public void removeReservation() {
200
202
		if(this.rc.getReservations().contains(this.reservation)) {
201
203
			this.rc.cancelReservation(this.reservation);
202
204
		}
203
205
		this.window.close();
204
206
	}
205
207
}
206
208

Challenge 6/Room.java

7 additions and 7 deletions.

View changes Hide changes
1
1
import java.util.HashSet;
2
2
import java.util.Date;
3
3
4
4
/**
5
5
 * A room in a hostel.
6
6
 * Room represents just that: A room.
7
7
 * A room contains a set of Beds, facilities that can be used, etc.
8
8
 * It's highly decoupled: Apart from holding a set of Beds, the only members
9
9
 * types consist of those that are available in every OpenJDK implementation.
10
10
 * @author Maarten Vangeneugden - 1438256
11
11
 */
12
12
public class Room {
13
13
14
14
	private HashSet<Bed> beds;
15
15
	private String type;
16
16
	private HashSet<String> facilities;
17
17
18
18
	/**
19
19
	 * Create a new Room.
20
20
	 * @param beds The amount of Beds that will be placed in this Room.
21
21
	 * @param type The type of this Room. (for example: Male, Female, Mixed,
22
22
	 * ...)
23
23
	 * @param facilities A Set of facilities this Room provides ("Shower",
24
24
	 * "Radio", "Overpriced WiFi", ...)
25
25
	 * @pre No parameter must be a null pointer.
26
26
	 * @pre beds must be at least 1.
27
27
	 * @pre Type mustn't be an empty String.
28
28
	 * @pre No facility may be an empty String.
29
29
	 * @post The Room will receive the provided amount of Beds, which are all
30
30
	 * completely released.
31
31
	 * @throws IllegalArgumentException if the amount of Beds is less than 1, or
32
32
	 * one of the facilities is an empty String.
33
33
	 * @throws NullPointerException if one of the parameters is a null pointer.
34
34
	 */
35
35
	public Room(int beds, String type, Set<String> facilities) {
36
36
		// Contract validation happens in the setter methods
37
37
		this.setFacilities(facilities);
38
38
		this.setType(type);
39
39
		if(beds < 1) {
40
40
			throw IllegalArgumentException("Beds was less than 1.");
41
-
		}
+
41
		}
42
42
		// Contract validated, execute constructor
43
43
		this.beds = new HashSet<>();
44
44
		for(int i=0; i<beds; i++) {
45
45
			this.beds.add(new Bed());
46
46
		}
47
47
	}
48
48
49
49
	/**
50
50
	 * Remove a Bed from this Room.
51
51
	 * This method will remove the given Bed from this Room.
52
52
	 * However, make sure that this Bed is free of any reservations, as you
53
53
	 * can't delete a Bed with a reserved period.
54
54
	 * @see Bed
55
55
	 * @param bed The Bed to be removed.
56
56
	 * @pre bed mustn't be null.
57
57
	 * @pre bed mustn't be the last Bed in this Room.
58
58
	 * @pre bed must be in this Room.
59
59
	 * @pre bed mustn't have any reserved periods.
60
60
	 * @post The amount of Beds in this Room is decremented by 1.
61
61
	 * @throws IllegalArgumentException if bed is not in this Room, or has
62
62
	 * reserved periods, or this is the last Bed in the Room.
63
63
	 * @throws NullPointerException if bed is a null pointer.
64
64
	 */
65
65
	public void removeBed(Bed bed) {
66
66
		// Contract validation
67
67
		if(!this.getBeds().contains(bed)) {
68
68
			throw IllegalArgumentException("The given Bed is not in this Room.");
69
-
		}
+
69
		}
70
70
		if(bed.hasReservations()) {
71
71
			throw IllegalArgumentException("The given Bed still has reserved periods.");
72
-
		}
+
72
		}
73
73
		if(this.getBeds().size() == 1) {
74
74
			throw IllegalArgumentException("Deleting this Bed would empty the Room.");
75
-
		}
+
75
		}
76
76
		// Contract validated, execute method
77
77
		this.beds.remove(bed);
78
78
		// Assert post conditions
79
79
		assert !this.getBeds().contains(bed) : "The given Bed was not removed from this Room.";
80
80
	}
81
81
82
82
	/**
83
83
	 * Add a Bed to this Room.
84
84
	 * @post The amount of Beds in this Room increments with 1.
85
85
	 */
86
86
	public void addBed() {
87
87
		this.beds.add(new Bed());
88
88
	}
89
89
90
90
	/**
91
91
	 * Returns a copy of all Beds.
92
92
	 * @return A verbatim copy of all Beds.
93
93
	 */
94
94
	@SuppressWarnings("unchecked")
95
95
	public Set<Bed> getBeds() {
96
96
		return (HashSet<Bed>) this.beds.clone();
97
97
	}
98
98
99
99
	/**
100
100
	 * Set the type of this Room.
101
101
	 * @param type The new type of the Room.
102
102
	 * @pre type mustn't be an empty String, or a null pointer.
103
103
	 * @post The Room's type is changed to the given type.
104
104
	 * @throws IllegalArgumentException if type is an empty String.
105
105
	 * @throws NullPointerException if type is a null pointer.
106
106
	 */
107
107
	public void setType(String type) {
108
108
		if(type.isEmpty()) {
109
109
			throw IllegalArgumentException("type is an empty String.");
110
-
		}
+
110
		}
111
111
		this.type = type;
112
112
	}
113
113
114
114
	/**
115
115
	 * Returns the type of this Room.
116
116
	 * @return The secret launch codes of the USS Nimitz, granting access to
117
117
	 * nuclear warheads so big, you'll lose faith in humanity.
118
118
	 */
119
119
	public String getType() {
120
120
		return type;
121
121
	}
122
122
123
123
	/**
124
124
	 * Set the facilities available in this Room.
125
125
	 * @param facilities The set of facilities in this Room.
126
126
	 * @pre No facility must be an empty String or a null pointer.
127
127
	 * @post The Room will have the newly provided facilities.
128
128
	 * @throws IllegalArgumentException if one of the facilities is an empty String.
129
129
	 * @throws NullPointerException if any given variable is a null pointer.
130
130
	 */
131
131
	public void setFacilities(Set<String> facilities) {
132
132
		for(String facility : facilities) {
133
133
			if(facility.isEmpty()) {
134
134
				throw IllegalArgumentException("A facility was an empty String.");
135
-
			}
+
135
			}
136
136
		}
137
137
		this.facilities = facilities;
138
138
	}
139
139
140
140
	/**
141
141
	 * Returns a copy of all facilities.
142
142
	 * @return A verbatim copy of all facilities.
143
143
	 */
144
144
	@SuppressWarnings("unchecked")
145
145
	public Set<String> getFacilities() {
146
146
		return (HashSet<String>)this.facilities.clone();
147
147
	}
148
148
149
149
	/**
150
150
	 * Search for Beds that can be reserved.
151
151
	 * This method will look through all Beds, and check whether they can be
152
152
	 * reserved in the given period. You will receive all Beds that can be
153
153
	 * reserved.
154
154
	 * @param begin The begin date.
155
155
	 * @param end The end date.
156
156
	 * @pre No parameter must be null.
157
157
	 * @pre begin must strictly come before end.
158
158
	 * @throws IllegalArgumentException if begin comes after end, or both are
159
159
	 * equal.
160
160
	 * @throws NullPointerException if any given parameter is a null pointer.
161
161
	 * @return A Set of all Beds that can be reserved, or an empty Set if no Bed
162
162
	 * can be reserved in the given period.
163
163
	 */
164
164
	public Set<Bed> getEmptyBeds(Date begin, Date end) {
165
165
		// Contract validation
166
166
		if(!begin.before(end)) {
167
167
			throw IllegalArgumentException("begin does not come strictly before end");
168
-
		}
+
168
		}
169
169
		// Validated
170
170
		Set<Bed> emptyBeds = new HashSet<>();
171
171
		for(Bed bed: this.getBeds()) {
172
172
			if(bed.isFree(begin, end)) {
173
173
				emptyBeds.add(bed);
174
174
			}
175
175
		}
176
176
		return emptyBeds;
177
177
	}
178
178
179
179
}
180
180

Challenge 6/RoomController.java

2 additions and 2 deletions.

View changes Hide changes
1
1
import java.util.Date;
2
2
import java.util.HashSet;
3
3
4
4
/**
5
5
 * Holds all Rooms in the hostel.
6
6
 * The hostel contains a set of Rooms, with facilities and stuff.
7
7
 * This class takes care of storing all those Rooms, and provides methods to
8
8
 * manage this, like removing Rooms, adding Rooms, ...
9
9
 * @author Maarten Vangeneugden - 1438256
10
10
 */
11
11
public class RoomController {
12
12
13
13
	private Set<Room> rooms;
14
14
15
15
	public RoomController() {
16
16
		this.rooms = new HashSet<>();
17
17
	}
18
18
19
19
20
20
	public Set<Room> getRooms() {
21
21
		return rooms;
22
22
	}
23
23
24
24
	/**
25
25
	 * Returns all rooms that meet the given requirements.
26
26
	 * This method will search through all rooms, and check which rooms qualify.
27
27
	 * Currently, this method checks for the following things:
28
28
	 * - Is the requested type available?
29
29
	 * - Does the Set of requested facilities form a subset of the Room's
30
30
	 *   facilities?
31
31
	 * - Are there any Beds in the Room that can be reserved in the given
32
32
	 *   period?
33
33
	 * @param reservation The Reservation for which to find eligible rooms.
34
34
	 * @pre reservation mustn't be null.
35
35
	 * @throws NullPointerException if reservation is a null pointer.
36
36
	 * @return A set of all rooms that meet the requirements, or an empty set if
37
37
	 * none were found.
38
38
	 */
39
39
	public Set<Room> getQualifiedRooms(Reservation reservation) {
40
40
		Set<Room> qualifiedRooms = new HashSet<>();
41
41
		for(Room room : this.getRooms()) {
42
42
			if(room.getType().equals(reservation.getType()))
43
-
				continue;
+
43
				continue;
44
44
			if(!room.getFacilities().containsAll(reservation.getFacilities()))
45
-
				continue;
+
45
				continue;
46
46
			if(room.getEmptyBeds(reservation.getBegin(), reservation.getEnd()).isEmpty())
47
47
				continue;
48
48
			// The Room fulfills all requirements at this point, so add it to
49
49
			// the set
50
50
			qualifiedRooms.add(room);
51
51
		}
52
52
		return qualifiedRooms;
53
53
	}
54
54
}
55
55

Challenge 6/Window.java

1 addition and 1 deletion.

View changes Hide changes
1
1
 * Window.java - Module to create a new window with JSugar.
2
2
 * Copyright © 2016 Maarten "Vngngdn" Vangeneugden
3
3
 * 
4
4
 * This program is free software: you can redistribute it and/or modify
5
5
 * it under the terms of the GNU General Public License as published by
6
6
 * the Free Software Foundation, either version 3 of the License, or
7
7
 * (at your option) any later version.
8
8
 * 
9
9
 * This program is distributed in the hope that it will be useful,
10
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
12
 * GNU General Public License for more details.
13
13
 * 
14
14
 * You should have received a copy of the GNU General Public License
15
15
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
16
 */
17
17
18
18
/*
19
19
 * TODO list:
20
20
 * - JSlider (It's the same as the JSpinner, only longer. So an extra.)
21
21
 * - JTable (And a JScrollBar to accompany it) (extra, because of JList)
22
22
 * - JFileChooser (?)
23
23
 * DONE list:
24
24
 * - JLabel
25
25
 * - JText
26
26
 * - JButton
27
27
 * - JDialogBoxes (you know, everything dialog related)
28
28
 * - JCheckbox
29
29
 * - JRadioButton (properly grouping them has been taken care of as well)
30
30
 * - JSpinner
31
31
 * - JComboBox
32
32
 * - JList
33
33
 */
34
34
35
35
import javax.swing.*; // FIXME: Maybe namespacing it to "javax.swing;" is a better idea.
36
36
import java.util.NoSuchElementException;
37
37
import java.lang.reflect.Method;
38
38
import java.io.File;
39
39
import java.awt.event.WindowEvent; // For programatically closing the window
40
40
41
41
/**
42
42
 * Window class for the program.
43
43
 *
44
44
 * Window contains the necessary data and methods to present the user with what
45
45
 * he's familiar with as being a "window". To make it functional, the developer
46
46
 * can make use of a series of methods to add components to said window, remove
47
47
 * components, and so on.
48
48
 * Currently, Window also contains methods to show dialogs. This will be cleaned
49
49
 * in the near future.
50
50
 * @author Maarten Vangeneugden
51
51
 */
52
52
public class Window { // Must be public, in order to generate Javadoc.
53
53
	private JPanel panel; // The panel that contains all the components.
54
54
	private JFrame frame; // The "window" being presented to the user.
55
55
56
56
	/**
57
57
	 * Constructor of Window.
58
58
	 * By creating a new Window instance, this constructor will automatically
59
59
	 * start the initialization of the GUI. After doing so, the caller can
60
60
	 * start adding components to the window as pleased.
61
61
	 * @param title The title to be shown in the window's title bar.
62
62
	 */
63
63
	public Window(String title) {
64
64
		// Setting the UI style to the platform's UI style. Fuck Swing's,
65
65
		// really.
66
66
		try {
67
67
		UIManager.setLookAndFeel(
68
68
				UIManager.getSystemLookAndFeelClassName());
69
69
		} catch(Exception e) {
70
70
			e.printStackTrace();
71
71
		}
72
72
73
73
		if(title == null || title.equals("")) { // If the title was omitted:
74
74
			title = "JSugar";
75
75
		}
76
76
		this.panel = new JPanel();
77
77
		// TODO: The current title is "Hello world!" but that will become caller
78
78
		// defined soon.
79
79
		JFrame frame = new JFrame(title);
80
80
		// Makes it so that if the user clicks the X in the titlebar, the window
81
81
		// closes:
82
82
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // XXX: Closes
83
83
		//ALL open windows!
84
84
		//frame.getContentPane().add(lblHelloWorld); // So you use a get() in order to set() data? #JavaWTF
85
85
		frame.setContentPane(this.panel); // Connecting the component panel to the window.
86
86
		// Makes the window fit to the necessary width and height, so it can show all "subcomponents".
87
87
		frame.pack(); 	
88
88
		frame.setVisible(true); // Makes the window visible to the user.
89
89
		this.frame = frame;
90
90
	}
91
91
92
92
	/**
93
93
	 * Resizes the window to fit all components.
94
94
	 * By calling this method, the window will evaluate the currently visible
95
95
	 * components, and resize itself so that all components become properly
96
96
	 * visible.
97
97
	 */
98
98
	private void updateWindow() {
99
99
		this.frame.pack();
100
100
	}
101
101
102
102
	/**
103
103
	 * A series of tests for method and class handling.
104
104
	 * When a caller presents certain methods with data concerning reflection,
105
105
	 * the Java classes you need to use for that are quite opaque, and don't
106
106
	 * offer much safety in any way.
107
107
	 * The solution therefore, is run some validation checks, but these take up
108
108
	 * a decent amount of space in terms of LoC.
109
109
	 * This method takes care of all that. Call this function whenever data
110
110
	 * needs to be validated.
111
111
	 * @param methodName The name of the method, as it is declared in object.
112
112
	 * @param object The class instance in where this method will be called.
113
113
	 * @return The method that could be derived from the supplied data, or null
114
114
	 * if that wasn't possible.
115
115
	 * @throws NullPointerException if either methodName or object are null
116
116
	 * pointers.
117
117
	 * @throws IllegalArgumentException if methodName is empty, or the method
118
118
	 * does not appear to be declared in the given object, or object is not a
119
119
	 * class.
120
120
	 */
121
121
	// All unchecked typecasts are safe, and the use of raw types is taken care
122
122
	// of.
123
123
	@SuppressWarnings({"unchecked","rawtypes"})
124
124
	private Method handleReflectionData(String methodName, Object object) {
125
125
		// Null pointer checking:
126
126
		if (methodName == null || object == null) {
127
127
			throw new NullPointerException("One or more of the given parameters are null pointers.");
128
128
		}
129
129
130
130
		// XXX: Some might say the next line should be in an else{} block. But
131
131
		// Scoping rules require that I'd then have to wrap the rest of the
132
132
		// method in the same else to use it.
133
133
		Class methodClass = object.getClass(); 
134
134
		if (methodName.equals("")) {
135
135
			throw new IllegalArgumentException("The given methodName was empty.");
136
136
		}
137
137
		Method method;
138
138
		try { // First: Look if there's a method without parameters.
139
139
			method = methodClass.getMethod(methodName, null);
140
140
		}
141
141
		catch (NoSuchMethodException exception) {
142
142
			try {
143
143
				// It's possible that the method requires an event parameter, so
144
144
				// check for that as well:
145
145
				Class<?>[] parameters = {java.awt.event.ActionEvent.class};
146
146
				method = methodClass.getMethod(methodName, parameters);
147
147
			}
148
148
			catch (NoSuchMethodException e) {
149
149
				throw new IllegalArgumentException("The given method does not appear in the given class. Be aware that the given method mustn't have any parameters, or only 1 parameter, which has to be of type java.awt.event.ActionEvent.");
150
150
			}
151
151
		}
152
152
		// At this stage, the given data has been validated, and we've been able
153
153
		// to retrieve the method itself.
154
154
		return method;
155
155
	}
156
156
157
157
	/**
158
158
	 * Creates a button in the GUI for interaction.
159
159
	 * This function offers a convenient way to create a button, that can be
160
160
	 * directly interacted with by the user. After creation, the button itself
161
161
	 * is returned to the caller, if he wishes to do something else with it.
162
162
	 * @param text The text that will be displayed in the button.
163
163
	 * @param action The action that will be returned to the action listener.
164
164
	 * @param methodName The name of the method that will be called when an
165
165
	 * action is triggered.
166
166
	 * @param triggerObject The object instance that contains the given method.
167
167
	 * This may only be a null pointer if triggerMethod is not an instance
168
168
	 * method.
169
169
	 * performed. This method may accept an ActionEvent parameter as its only
170
170
	 * parameter, or no parameters at all.
171
171
	 * @throws NullPointerException if triggerMethod is a null pointer, or
172
172
	 * the empty String was given.
173
173
	 * @throws IllegalArgumentException if triggerMethod has more than 1
174
174
	 * parameter, or the 1 required parameter is not of type ActionEvent.
175
175
	 * @return The button that was created.
176
176
	 * @see java.awt.event.ActionEvent
177
177
	 * @see java.lang.reflect.Method#invoke
178
178
	 */
179
179
	public JButton createButton(String text, String action, String methodName, Object triggerObject) {
180
180
		Method triggerMethod = this.handleReflectionData(methodName, triggerObject);
181
181
182
182
		// For starters, we first assert that the given parameters are valid:
183
183
		if (text == null) {
184
184
			text = "";
185
185
		}
186
186
		if (action == null) {
187
187
			action = "";
188
188
		}
189
189
		
190
190
		// When the method gets here, everything's been validated correctly.
191
191
		JButton button = new JButton(text);
192
192
		button.setActionCommand(action);
193
193
		button.addActionListener(
194
194
				new java.awt.event.ActionListener() {
195
195
					public void actionPerformed(java.awt.event.ActionEvent event) {
196
196
						try {
197
197
							triggerMethod.setAccessible(true);
198
198
							if (triggerMethod.getParameterTypes().length == 0) {
199
199
								// FIXME: Next line throws a warning?
200
200
								triggerMethod.invoke(triggerObject, null);
201
201
							}
202
202
							else {
203
203
								triggerMethod.invoke(triggerObject, new Object[]{event});
204
204
							}
205
205
						}
206
206
						catch (Exception useless) {
207
207
							/*
208
208
							 * XXX: Some info on why I don't just throw said
209
209
							 * Exception to the caller:
210
210
							 * Java has this awful language constraint, which
211
211
							 * forces every damn exception that isn't a subclass
212
212
							 * of RuntimeException, to be declared in the method
213
213
							 * declaration. This tends to infect all underlying
214
214
							 * methods as well, and all that for reasons I can't
215
215
							 * comprehend. In order to keep JSugar a simple and
216
216
							 * clean library, I'll rather just handle it here,
217
217
							 * and throw a RuntimeException with appropriate
218
218
							 * details.
219
219
							 */
220
220
							throw new IllegalArgumentException("triggerMethod is not accessible from this context.");
221
221
						}
222
222
					}
223
223
				});
224
224
		this.addComponent(button);
225
225
		return button;
226
226
	}
227
227
228
228
	/**
229
229
	 * Ask the user for input through a dialog box.
230
230
	 * This method presents the user with an input field, that can accept
231
231
	 * textual input. The method will return the given input after the user's
232
232
	 * clicked a button to send.
233
233
	 * @param text The text/question to be asked to the user.
234
234
	 * @return A String, equal to what the user entered.
235
235
	 * @throws NullPointerException if text is a null pointer.
236
236
	 */
237
237
	public String inputDialog(String text) {
238
238
		if (text == null) {
239
239
			throw new NullPointerException("The given text/question was a null pointer.");
240
240
		}
241
241
		return JOptionPane.showInputDialog(text);
242
242
	}
243
243
244
244
	/**
245
245
	 * Give the user a dialog box.
246
246
	 * This method can be used to provide a simple dialog to the user.
247
247
	 * This will show the user the given question, after which a boolean value
248
248
	 * is returned, holding the choice.
249
249
	 * @param text The text/question to be asked to the user.
250
250
	 * @return True if the user confirms, False if he denies.
251
251
	 * @throws NullPointerException if text is a null pointer.
252
252
	 */
253
253
	public boolean confirmDialog(String text) {
254
254
		if (text == null) {
255
255
			throw new NullPointerException("The given text/question was a null pointer.");
256
256
		}
257
257
		final int ACCEPTED = 0;
258
258
		//final int DENIED = 1; // Not used in the current context.
259
259
		int result = this.choiceDialog(text, new String[]{"Confirm", "Deny"});
260
260
		if (result == ACCEPTED) {
261
261
			return true;
262
262
		}
263
263
		else {
264
264
			return false;
265
265
		}
266
266
	}
267
267
268
268
	/**
269
269
	 * Give the user a choice dialog box.
270
270
	 * This method gives the user a simple dialog with predefined choices.
271
271
	 * These choices are to be provided by the caller in a simple array.
272
272
	 *
273
273
	 * Tip: This method works extremely well with arbitrary created choices.
274
274
	 * That is: if the outcome of the dialog is trivial (e.g. Only 1 choice),
275
275
	 * then that value is immediately returned.
276
276
	 * @param text The text/question to be asked to the user.
277
277
	 * @param choices An array of Strings, containing the choices the user can
278
278
	 * pick.
279
279
	 * @return The index value of the picked choice, or -1 if no choices were
280
280
	 * given.
281
281
	 * @throws NullPointerException if text is a null pointer.
282
282
	 */
283
283
	public int choiceDialog(String text, String[] choices) {
284
284
		if (text == null) {
285
285
			throw new NullPointerException("The given text/question was a null pointer.");
286
286
		}
287
287
		// First: handling the trivial cases:
288
288
		if (choices.length == 0) {
289
289
			return -1;
290
290
		}
291
291
		else if (choices.length == 1) {
292
292
			return 0;
293
293
		}
294
294
		int answer = JOptionPane.CLOSED_OPTION;
295
295
		// The dialog needs to be shown again until the user has made a possible
296
296
		// choice, i.e. Chickening out using the close button is not possible
297
297
		// (Because that returns CLOSED_OPTION).
298
298
		while (answer == JOptionPane.CLOSED_OPTION) {
299
299
				JOptionPane.showOptionDialog(
300
300
					null, // The parent component. May become the panel?
301
301
					text, // The text/question to describe the goal
302
302
					"Dialog", // The text in the title bar
303
303
					JOptionPane.DEFAULT_OPTION, // The kind of available options
304
304
					JOptionPane.QUESTION_MESSAGE, // The type of message
305
305
					null, // The icon to show
306
306
					choices, // The possible choices
307
307
					choices[0] // The standard choice
308
308
					);
309
309
		}
310
310
		return answer;
311
311
	}
312
312
		
313
313
314
314
	/**
315
315
	 * Creates a label in the GUI for interaction.
316
316
	 * This function offers a convenient way to create a label, that can be
317
317
	 * directly interacted with by the user. After creation, the label itself
318
318
	 * is returned to the caller, if he wishes to do something else with it.
319
319
	 * @param text The text that will be displayed in the label.
320
320
	 * @return The label that was created.
321
321
	 */
322
322
	public JLabel createLabel(String text) {
323
323
		JLabel label = new JLabel(text);
324
324
		this.addComponent(label);
325
325
		return label;
326
326
	}
327
327
328
328
	/**
329
329
	 * Adds a checkbox to the window.
330
330
	 * By providing a String, you can use this method to easily
331
331
	 * create a checkbox, and add it to the window. 
332
332
	 * @param text The text to put next to the checkbox.
333
333
	 * @return The checkbox that was created and added to the GUI.
334
334
	 */
335
335
	public JCheckBox createCheckbox(String text) {
336
336
		JCheckBox checkbox = new JCheckBox(text);
337
337
		this.addComponent(checkbox);
338
338
		return checkbox;
339
339
	}
340
340
341
341
	/**
342
342
	 * Adds radio buttons to the window.
343
343
	 * Given a list of Strings, this method will create the same amount of radio
344
344
	 * buttons.
345
345
	 *
346
346
	 * The radio buttons will silently be grouped in a ButtonGroup object,
347
347
	 * making them automatically disable each other, so only 1 radio button can
348
348
	 * be enabled. This ButtonGroup is immutable.
349
349
	 *
350
350
	 * If you need a mutable ButtonGroup, create your own, and use the 
351
351
	 * {@link #addComponent} method to add the radio buttons manually.
352
352
	 * @param text An array of Strings. The length of the array will determine
353
353
	 * the amount of radio buttons that will be created.
354
354
	 * @return An array of radio buttons, in the same order as text.
355
355
	 */
356
356
	public JRadioButton[] createRadioButtons(String text[]) {
357
357
		JRadioButton[] radioButtons = new JRadioButton[text.length];
358
358
		ButtonGroup buttonGroup = new ButtonGroup();
359
359
		for (int i=0; i<radioButtons.length; i++) {
360
360
			radioButtons[i] = new JRadioButton(text[i]);
361
361
			//radioButtons[i].setText(text[i]);
362
362
			buttonGroup.add(radioButtons[i]);
363
363
			this.addComponent(radioButtons[i]);
364
364
		}
365
365
366
366
		assert radioButtons.length == buttonGroup.getButtonCount() : "The amount of radio buttons ("+ radioButtons.length +") differs from the amount of buttons in buttonGroup ("+ buttonGroup.getButtonCount() +").";
367
367
		return radioButtons;
368
368
	}
369
369
370
370
	public JTextField createTextField(String text) {
371
371
		JTextField textField = new JTextField(text);
372
372
		this.addComponent(textField);
373
373
		return textField;
374
374
	}
375
375
376
376
	/**
377
377
	 * Adds a number spinner component to the GUI.
378
378
	 * This method allows the caller to create a so-called "spinner component"
379
379
	 * to the window. This spinner is an input box, in which only integers can
380
380
	 * be put.
381
381
	 *
382
382
	 * The caller can set a range, a start value, and a step size.
383
383
	 *
384
384
	 * The spinner created with this method may modify itself based on the
385
385
	 * parameters.
386
386
	 * For example: If the minimum and maximum value are equal, the spinner will
387
387
	 * be disabled.
388
388
	 *
389
389
	 * @param minimum The minimum value that can be selected.
390
390
	 * @param maximum The maximum value that can be selected.
391
391
	 * @param start The value that will initially be shown in the component.
392
392
	 * @param stepSize The step size when the user increases/decreases the
393
393
	 * value.
394
394
	 * @throws IllegalArgumentException if minimum is larger than maximum, 
395
395
	 * start is not in the range of the selectable values, or stepsize is not a
396
396
	 * natural number.
397
397
	 * @return The JSpinner that was added to the window.
398
398
	 */
399
399
	public JSpinner createSpinner(int minimum, int maximum, int start, int stepSize) {
400
400
		// As usual, we begin with checking the contract:
401
401
		if(minimum > maximum) {
402
402
			throw new IllegalArgumentException("The minimum value ("+ minimum +") was larger than the maximum value ("+ maximum +")");
403
403
		}
404
404
		// The "start ∉ [minimum, maximum]" is done by the SpinnerNumberModel
405
405
		// constructor, which will be constructed later.
406
406
		if(stepSize <= 0) { // stepSize ∉ ℕ¹ (In Belgium: ℕ₀)
407
407
			throw new IllegalArgumentException("The stepSize ("+ stepSize +") is not a natural number (excluding 0).");
408
408
		}
409
409
		// If the contract is valid, we can begin:
410
410
		/*
411
411
		 * I'd like to interject here, because this is a nice example of why
412
412
		 * JSugar was a good idea:
413
413
		 * If you want a spinner, you'll typically want an integer spinner. But
414
414
		 * in Swing, when you create a JSpinner, it creates a JSpinner, with a
415
415
		 * predefined 'SpinnerNumberModel' attached to it.
416
416
		 * It's this model you then have to extract from the created spinner, on
417
417
		 * which you need to apply the configuration.
418
418
		 * What you actually have to do, is create a SpinnerNumberModel
419
419
		 * yourself, put your settings on that, and then, create a JSpinner to
420
420
		 * which you give that SpinnerNumberModel.
421
421
		 * In essence: The entire Java framework is shit.
422
422
		 */
423
423
		SpinnerNumberModel spinnerSettings = new SpinnerNumberModel(
424
424
				start,
425
425
				minimum,
426
426
				maximum,
427
427
				stepSize
428
428
				);
429
429
		JSpinner spinner = new JSpinner(spinnerSettings);
430
430
		if(minimum == maximum) { // Trivial value is set already, --> disable.
431
431
			spinner.setEnabled(false);
432
432
		}
433
433
		this.addComponent(spinner);
434
434
		return spinner;
435
435
	}
436
436
437
437
	/**
438
438
	 * Adds a number spinner component to the GUI.
439
439
	 * This method allows the caller to create a so-called "spinner component"
440
440
	 * to the window. This spinner is an input box, in which only integers can
441
441
	 * be put.
442
442
	 * 
443
443
	 * Tip: This method is a convenience method, and works extremely well with
444
444
	 * arbitrary data.
445
445
	 * For example: The start value is automatically set to the minimal possible
446
446
	 * value, and the step size defaults to 1.
447
447
	 * If the minimum and maximum are equal, the component will be disabled, and
448
448
	 * thus, be locked on the only (trivially) possible value.
449
449
	 * If minimum is larger than maximum, the method will notify you of this,
450
450
	 * but also swap the values. So you can rest assured that the spinner will
451
451
	 * handle errors, but also, inform you about it.
452
452
	 * @param minimum The minimum value that can be selected.
453
453
	 * @param maximum The maximum value that can be selected.
454
454
	 * @return The JSpinner component that was added to the window.
455
455
	 */
456
456
	public JSpinner createSpinner(int minimum, int maximum) {
457
457
		// The disabling of equal values is done in the full createSpinner(), so
458
458
		// this is merely switching values if they need to be swapped.
459
459