OOP2

Whew, added a lot today.

Author
Maarten 'Vngngdn' Vangeneugden
Date
Nov. 18, 2016, 12:40 p.m.
Hash
e680d277849474ffb1b15dab80c79c124819276b
Parent
d72af86215ff4ce4739320d66e43d0bfbca4d3ec
Modified files
Challenge/Main.java
Challenge/MainWindow.java
Challenge/Reservation.java
Challenge/ReservationController.java
Challenge/ReservationView.java
Challenge/RoomController.java
Challenge/SearchView.java
Challenge/Window.java

Challenge/Main.java

11 additions and 0 deletions.

View changes Hide changes
+
1
 * The program starts here.
+
2
 * @author Maarten Vangeneugden - 1438256
+
3
 */
+
4
public class Main {
+
5
	public static void main(String[] args) {
+
6
		ReservationController rc = new ReservationController();
+
7
		RoomController roc = new RoomController();
+
8
		MainWindow mainWindow = new MainWindow(rc, roc);
+
9
	}
+
10
}
+
11

Challenge/MainWindow.java

25 additions and 0 deletions.

View changes Hide changes
+
1
+
2
	private ReservationController reservationController;
+
3
	private RoomController roomController;
+
4
	
+
5
	public MainWindow(ReservationController reservationController, RoomController roomController) {
+
6
		this.reservationController = reservationController;
+
7
		this.roomController = roomController;
+
8
+
9
		Window window = new Window("Main menu");
+
10
		window.createButton("New reservation", "", "addReservation", this);
+
11
		window.createButton("Search screen", "", "openSearchView", this);
+
12
	}
+
13
+
14
	public void openSearchView() {
+
15
		SearchView sv = new SearchView(this.reservationController, this.roomController);
+
16
	}
+
17
	
+
18
	public void addReservation() {
+
19
		Reservation reservation = new Reservation(this.reservationController, this.roomController);
+
20
		ReservationView rv = new ReservationView(reservation, this.reservationController, this.roomController);
+
21
+
22
	}
+
23
+
24
}
+
25

Challenge/Reservation.java

79 additions and 3 deletions.

View changes Hide changes
1
1
+
2
import java.util.Date;
+
3
2
4
public class Reservation {
3
5
4
6
	private String groupName;
5
7
	private Date date;
6
8
	private Set<Bed> reservedBeds;
7
9
	private int reservationID;
8
10
	private String roomType;
9
11
	private Set<String> roomFacilities;
10
12
	private int nights;
11
13
	private int[] breakfastDays;
12
14
+
15
	private RoomController roomController;
+
16
	private ReservationController reservationController;
+
17
+
18
	public Reservation(String groupName, Date date, Set<Bed> reservedBeds, String roomType, Set<String> roomFacilities, int nights, int[] breakfastDays, ReservationController reservationController, RoomController roomController) {
+
19
		this.reservationController = reservationController;
+
20
		this.roomController = roomController;
+
21
13
22
	public Reservation(String groupName, Date date, Set<Bed> reservedBeds, int reservationID, String roomType, Set<String> roomFacilities, int nights, int[] breakfastDays) {
14
-
		this.groupName = groupName;
15
23
		this.date = date;
16
24
		this.reservedBeds = reservedBeds;
17
25
		this.reservationID = reservationID;
18
-
		this.roomType = roomType;
+
26
		this.roomType = roomType;
19
27
		this.roomFacilities = roomFacilities;
20
28
		this.nights = nights;
21
29
	}
+
30
	}
+
31
+
32
	/**
+
33
	 * Creates an empty Reservation.
+
34
	 * This constructor is espcially useful for adding a new reservation.
+
35
	 */
+
36
	public Reservation(ReservationController reservationController, RoomController roomController) {
+
37
		this(
+
38
				"",
+
39
				new Date(),
+
40
				new HashSet<>(),
+
41
				"",
+
42
				new HashSet<>(),
+
43
				1,
+
44
				new int[0],
+
45
				reservationController,
+
46
				roomController
+
47
				);
+
48
	}
22
49
23
50
	public void setGroupName(String groupName) {
24
51
		this.groupName = groupName;
25
52
	}
26
53
27
54
	public String getGroupName() {
28
55
		return groupName;
29
56
	}
30
57
31
58
	public void setDate(Date date) {
32
59
		this.date = date;
33
60
	}
34
61
35
62
	public Date getDate() {
36
63
		return date;
37
64
	}
38
65
39
66
	public void setReservedBeds(Set<Bed> reservedBeds) {
40
67
		this.reservedBeds = reservedBeds;
41
68
	}
42
69
43
70
	public Set<Bed> getReservedBeds() {
44
71
		return reservedBeds;
45
72
	}
46
73
47
74
	public void setReservationID(int reservationID) {
48
75
		this.reservationID = reservationID;
49
76
	}
50
77
51
78
	public int getReservationID() {
52
79
		return reservationID;
53
80
	}
54
81
55
82
	public void setRoomType(String roomType) {
56
83
		this.roomType = roomType;
57
84
	}
58
85
59
86
	public String getRoomType() {
60
87
		return roomType;
61
88
	}
62
89
63
90
	public void setRoomFacilities(Set<String> roomFacilities) {
64
91
		this.roomFacilities = roomFacilities;
65
92
	}
66
93
67
94
	public Set<String> getRoomFacilities() {
68
95
		return roomFacilities;
69
96
	}
70
97
71
98
	public void setNights(int nights) {
72
99
		this.nights = nights;
73
100
	}
74
101
75
102
	public int getNights() {
76
103
		return nights;
77
104
	}
78
105
79
106
	// TODO breakfast get/set!
80
-
+
107
	 * Calculates the price of the Reservation, based on its current state.
+
108
	 * Price table:
+
109
	 * - 20/person/day
+
110
	 *   - 5 less in low season, 5 more in high season
+
111
	 * - 4/breakfast ordered
+
112
	 * - If room is fully booked by the group --> 10% discount
+
113
	 * @return The price in euros.
+
114
	 */
+
115
	public int getPrice() {
+
116
		int totalPrice = 0;
+
117
		// Jan - Apr: Mid
+
118
		// May - Aug: High
+
119
		// Sep - Dec: Low
+
120
		
+
121
		// Calculate bed prices
+
122
		int month = this.getDate().getMonth();
+
123
		int bedPrice = 20;
+
124
		if(month >=8) { // From September:
+
125
			bedPrice -= 5;
+
126
		} else if(month >=4) { // From May:
+
127
			bedPrice += 5;
+
128
		}
+
129
		totalPrice += (this.getReservedBeds().size() * this.getNights() * bedPrice);
+
130
		// Calculate price for breakfasts
+
131
		int breakfasts = this.getBreakfastDays().length;
+
132
		totalPrice += breakfasts * this.getReservedBeds().size();
+
133
		// Check if eligible for discount
+
134
		for(Room room: roomController.getRooms()) {
+
135
			Set<Bed> roomBeds = room.getBeds();
+
136
			if(roomBeds.containsAll(this.reservedBeds)) {
+
137
				double discount = (double)totalPrice * 0.1;
+
138
				totalPrice -= (int)discount;
+
139
			}
+
140
		}
+
141
		return totalPrice;
+
142
	}
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
	public void setBreakfastDays(int[] breakfastDays) {
+
151
		this.breakfastDays = breakfastDays;
+
152
	}
+
153
	public int[] getBreakfastDays() {
+
154
		return this.breakfastDays;
+
155
	}
+
156
81
157
}
82
158

Challenge/ReservationController.java

54 additions and 0 deletions.

View changes Hide changes
+
1
import java.util.HashSet;
+
2
import java.util.Date;
+
3
+
4
public class ReservationController {
+
5
+
6
	private Set<Reservation> reservations;
+
7
+
8
	public ReservationController() {
+
9
		this.reservations = new HashSet<>();
+
10
	}
+
11
+
12
	public void setReservations(Set<Reservation> reservations) {
+
13
		this.reservations = reservations;
+
14
	}
+
15
+
16
	public Set<Reservation> getReservations() {
+
17
		return reservations;
+
18
	}
+
19
+
20
	public int generateReservationID() {
+
21
		int ID = 0;
+
22
		while(true) {
+
23
			for(Reservation reservation: this.reservations) {
+
24
				if(reservation.getReservationID() == ID) {
+
25
					ID++;
+
26
					continue;
+
27
				}
+
28
			}
+
29
			break;
+
30
		}
+
31
		// Test:
+
32
		for(Reservation reservation: this.reservations) {
+
33
			assert reservation.getReservationID() != ID : "Duplicate ID generated!";
+
34
		}
+
35
		return ID;
+
36
	}
+
37
+
38
	/**
+
39
	 * Adds and confirms the reservation.
+
40
	 * By calling this method, the given reservation will be stored in the
+
41
	 * system, and the given room will be filled.
+
42
	 * @pre room must have enough empty beds
+
43
	 * @pre no parameter may be null
+
44
	 */
+
45
	public void addReservation(Reservation reservation, Room room) {
+
46
		// TODO: Add reservation to the set, and add the reserved days to the
+
47
		// beds in the given room.
+
48
	}
+
49
	public void cancelReservation(Reservation reservation) {
+
50
		// TODO: Remove from controller list; release beds, ...
+
51
	}
+
52
+
53
}
+
54

Challenge/ReservationView.java

157 additions and 0 deletions.

View changes Hide changes
+
1
import java.util.Set;
+
2
import java.util.HashSet;
+
3
import java.util.Date;
+
4
/**
+
5
 * Creates a view screen that allows interaction with the reservation.
+
6
 * This window will place the reservation details in editable fields, so its
+
7
 * state can be adapted.
+
8
 * It then also adds a couple of buttons; add/update/cancel, with their
+
9
 * respective actions. These will then be passed to the ReservationController.
+
10
 * It will not allow to add/update a reservation if dates overlap, i.e. beds
+
11
 * can't be reserved for that period.
+
12
 */
+
13
public class ReservationView {
+
14
+
15
	private Reservation reservation;
+
16
	private Window window;
+
17
+
18
	private ReservationController rc;
+
19
	private RoomController roc;
+
20
+
21
	// GUI widgets:
+
22
	private JTextField nameField;
+
23
	private JSpinner amountPeopleField;
+
24
	private JTextField dateField;
+
25
	private JSpinner durationField;
+
26
	private JRadioButton[] typeField;
+
27
	private JCheckBox showerField;
+
28
	private JCheckBox bathField;
+
29
	private JCheckBox minibarField;
+
30
	private JCheckBox aircoField;
+
31
+
32
+
33
	public ReservationView(Reservation reservation, ReservationController rc, RoomController roc) {
+
34
		this.rc = rc;
+
35
		this.roc = roc;
+
36
+
37
		this.reservation = reservation;
+
38
		this.window = new Window("Reservation screen");
+
39
		this.addFields();
+
40
	}
+
41
+
42
	public void setReservation(Reservation reservation) {
+
43
		this.reservation = reservation;
+
44
	}
+
45
+
46
	public Reservation getReservation() {
+
47
		return reservation;
+
48
	}
+
49
+
50
	private void addFields() {
+
51
		this.window.createLabel("Group name");
+
52
		this.nameField = window.createTextField(this.reservation.getGroupName());
+
53
		this.window.createLabel("Amount of people");
+
54
		this.amountPeopleField = window.createSpinner(1, 20);
+
55
		// Formatting date for the date field:
+
56
		this.window.createLabel("date");
+
57
		Date date = this.reservation.getDate();
+
58
		String day = String.valueOf(date.getDate());
+
59
		String month = String.valueOf(date.getMonth()+1);
+
60
		String year = String.valueOf(date.getYear()+1900);
+
61
		this.dateField = window.createTextField(day +" "+ month +" "+ year);
+
62
		this.window.createLabel("Duration");
+
63
		this.durationField = window.createSpinner(1, 355);
+
64
		String[] types = new String[3];
+
65
		types[0] = "Male";
+
66
		types[1] = "Female";
+
67
		types[2] = "Mixed";
+
68
		this.typeField = this.window.createRadioButtons(types);
+
69
		this.showerField = this.window.createCheckbox("Shower");
+
70
		this.bathField = this.window.createCheckbox("Bath");
+
71
		this.minibarField = this.window.createCheckbox("Minibar");
+
72
		this.aircoField = this.window.createCheckbox("Airco");
+
73
+
74
+
75
		this.window.createButton("Add/Update reservation", "", "addReservation", this);
+
76
		this.window.createButton("Cancel reservation", "", "cancelReservation", this);
+
77
	}
+
78
+
79
	
+
80
+
81
	public void addReservation() {
+
82
		// Collect all data from the fields
+
83
		String name = this.nameField.getText();
+
84
		String date = this.dateField.getText();
+
85
		// Extracting date data:
+
86
		String[] dateParts = date.split(" ");
+
87
		int day = Integer.parseInt(dateParts[0]);
+
88
		int month = Integer.parseInt(dateParts[1]);
+
89
		int year = Integer.parseInt(dateParts[2]);
+
90
		Date actualDate = new Date(day, month-1, year-1900);
+
91
+
92
		int amount = (Integer)this.amountPeopleField.getValue();
+
93
		int duration = (Integer)this.durationField.getValue();
+
94
		String type = "";
+
95
		for(int i=0; i<this.typeField.length; i++) {
+
96
			if(this.typeField[i].isSelected()) {
+
97
				type = this.typeField[i].getText();
+
98
				break;
+
99
			}
+
100
		}
+
101
		Set<String> facilities = new HashSet<>();
+
102
		if(this.showerField.isSelected()) {
+
103
			facilities.add("Shower");
+
104
		}
+
105
		if(this.bathField.isSelected()) {
+
106
			facilities.add("Bath");
+
107
		}
+
108
		if(this.minibarField.isSelected()) {
+
109
			facilities.add("Minibar");
+
110
		}
+
111
		if(this.aircoField.isSelected()) {
+
112
			facilities.add("Airco");
+
113
		}
+
114
+
115
		Set<Room> possibleRooms = this.roc.getQualifiedRooms(actualDate, duration, type, facilities);
+
116
		if(possibleRooms.size() == 0) {
+
117
			boolean tryAgain = this.window.confirmDialog("No rooms met the requirements! Would you like to continue and change the parameters?");
+
118
			if(!tryAgain) {
+
119
				// TODO close window
+
120
			}
+
121
		}
+
122
		else {
+
123
			// Determine end date:
+
124
			long beginDate = actualDate.getTime();
+
125
			long endDate = beginDate + (duration * 24 * 60 * 60 * 1000);
+
126
			Date actualEndDate = new Date(endDate);
+
127
			Room pickedRoom = null;
+
128
			for(Room room: possibleRooms) {
+
129
				if(room.getEmptyBeds(actualDate, actualEndDate).size() < room.getBeds().size()) {
+
130
					// First, fill the rooms that are partially filled.
+
131
					pickedRoom = room;
+
132
					break;
+
133
				}
+
134
			}
+
135
			if(pickedRoom == null) { // If still no room, pick an empty room
+
136
				for(Room room: possibleRooms) {
+
137
					if(room.getEmptyBeds(actualDate, actualEndDate).size() >= amount) {
+
138
						pickedRoom = room;
+
139
						break;
+
140
					}
+
141
				}
+
142
			}
+
143
			assert pickedRoom != null;
+
144
			// TODO: Set reservation fields here!
+
145
			this.rc.addReservation(reservation, pickedRoom);
+
146
+
147
			// Confirm and show price:
+
148
			int price = this.reservation.getPrice();
+
149
			this.window.confirmDialog("Reservation confirmed! Price: " +String.valueOf(price));
+
150
		}
+
151
		
+
152
	}
+
153
	public void cancelReservation() {
+
154
		// TODO: Close the window. That's all.
+
155
	}
+
156
}
+
157

Challenge/RoomController.java

40 additions and 0 deletions.

View changes Hide changes
+
1
import java.util.Date;
+
2
import java.util.HashSet;
+
3
+
4
/**
+
5
 * Holds all Rooms in the hostel.
+
6
 */
+
7
public class RoomController {
+
8
+
9
	private Set<Room> rooms;
+
10
+
11
	public RoomController() {
+
12
		this.rooms = new HashSet<>();
+
13
	}
+
14
+
15
	public void setRooms(Set<Room> rooms) {
+
16
		this.rooms = rooms;
+
17
	}
+
18
+
19
	public Set<Room> getRooms() {
+
20
		return rooms;
+
21
	}
+
22
+
23
	/**
+
24
	 * Returns all rooms that meet the given requirements.
+
25
	 * This method will search through all rooms, and check which rooms qualify.
+
26
	 * @return A set of all rooms that meet the requirements, or an empty set if
+
27
	 * none were found.
+
28
	 */
+
29
	public Set<Room> getQualifiedRooms(Date begin, int duration, String type, Set<String> facilities) {
+
30
		Set<Room> qualifiedRooms = new HashSet<>();
+
31
		long beginDate = begin.getTime();
+
32
		long endDate = beginDate + (duration * 24 * 60 * 60 * 1000);
+
33
		Date end = new Date(endDate);
+
34
+
35
		// TODO: Loop through all rooms that qualify, collect them, and return
+
36
		// that set.
+
37
		return qualifiedRooms;
+
38
	}
+
39
}
+
40

Challenge/SearchView.java

22 additions and 0 deletions.

View changes Hide changes
+
1
	private ReservationController rc;
+
2
	private RoomController roc;
+
3
	private Window window;
+
4
+
5
	public SearchView(ReservationController rc, RoomController roc) {
+
6
		this.rc = rc;
+
7
		this.roc = roc;
+
8
+
9
		this.window = new Window("Search screen");
+
10
		this.addFields();
+
11
	}
+
12
	
+
13
	private void addFields() {
+
14
		// TODO: Add fields to private members!
+
15
		this.window.createLabel("Reservation number:");
+
16
		this.window.createTextField("");
+
17
		this.window.createLabel("Group name:");
+
18
		this.window.createTextField("");
+
19
+
20
	}
+
21
}
+
22

Challenge/Window.java

641 additions and 0 deletions.

View changes Hide changes
+
1
 * Window.java - Module to create a new window with JSugar.
+
2
 * Copyright © 2016 Maarten "Vngngdn" Vangeneugden
+
3
 * 
+
4
 * This program is free software: you can redistribute it and/or modify
+
5
 * it under the terms of the GNU General Public License as published by
+
6
 * the Free Software Foundation, either version 3 of the License, or
+
7
 * (at your option) any later version.
+
8
 * 
+
9
 * This program is distributed in the hope that it will be useful,
+
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
+
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
12
 * GNU General Public License for more details.
+
13
 * 
+
14
 * You should have received a copy of the GNU General Public License
+
15
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
16
 */
+
17
+
18
/*
+
19
 * TODO list:
+
20
 * - JSlider (It's the same as the JSpinner, only longer. So an extra.)
+
21
 * - JTable (And a JScrollBar to accompany it) (extra, because of JList)
+
22
 * - JFileChooser (?)
+
23
 * DONE list:
+
24
 * - JLabel
+
25
 * - JText
+
26
 * - JButton
+
27
 * - JDialogBoxes (you know, everything dialog related)
+
28
 * - JCheckbox
+
29
 * - JRadioButton (properly grouping them has been taken care of as well)
+
30
 * - JSpinner
+
31
 * - JComboBox
+
32
 * - JList
+
33
 */
+
34
+
35
import javax.swing.*; // FIXME: Maybe namespacing it to "javax.swing;" is a better idea.
+
36
import java.util.NoSuchElementException;
+
37
import java.lang.reflect.Method;
+
38
import java.io.File;
+
39
/**
+
40
 * Window class for the program.
+
41
 *
+
42
 * Window contains the necessary data and methods to present the user with what
+
43
 * he's familiar with as being a "window". To make it functional, the developer
+
44
 * can make use of a series of methods to add components to said window, remove
+
45
 * components, and so on.
+
46
 * Currently, Window also contains methods to show dialogs. This will be cleaned
+
47
 * in the near future.
+
48
 * @author Maarten Vangeneugden
+
49
 */
+
50
public class Window { // Must be public, in order to generate Javadoc.
+
51
	private JPanel panel; // The panel that contains all the components.
+
52
	private JFrame frame; // The "window" being presented to the user.
+
53
+
54
	/**
+
55
	 * Constructor of Window.
+
56
	 * By creating a new Window instance, this constructor will automatically
+
57
	 * start the initialization of the GUI. After doing so, the caller can
+
58
	 * start adding components to the window as pleased.
+
59
	 * @param title The title to be shown in the window's title bar.
+
60
	 */
+
61
	public Window(String title) {
+
62
		// Setting the UI style to the platform's UI style. Fuck Swing's,
+
63
		// really.
+
64
		try {
+
65
		UIManager.setLookAndFeel(
+
66
				UIManager.getSystemLookAndFeelClassName());
+
67
		} catch(Exception e) {
+
68
			e.printStackTrace();
+
69
		}
+
70
+
71
		if(title == null || title.equals("")) { // If the title was omitted:
+
72
			title = "JSugar";
+
73
		}
+
74
		this.panel = new JPanel();
+
75
		// TODO: The current title is "Hello world!" but that will become caller
+
76
		// defined soon.
+
77
		JFrame frame = new JFrame(title);
+
78
		// Makes it so that if the user clicks the X in the titlebar, the window
+
79
		// closes:
+
80
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // XXX: Closes
+
81
		//ALL open windows!
+
82
		//frame.getContentPane().add(lblHelloWorld); // So you use a get() in order to set() data? #JavaWTF
+
83
		frame.setContentPane(this.panel); // Connecting the component panel to the window.
+
84
		// Makes the window fit to the necessary width and height, so it can show all "subcomponents".
+
85
		frame.pack(); 	
+
86
		frame.setVisible(true); // Makes the window visible to the user.
+
87
		this.frame = frame;
+
88
	}
+
89
+
90
	/**
+
91
	 * Resizes the window to fit all components.
+
92
	 * By calling this method, the window will evaluate the currently visible
+
93
	 * components, and resize itself so that all components become properly
+
94
	 * visible.
+
95
	 */
+
96
	private void updateWindow() {
+
97
		this.frame.pack();
+
98
	}
+
99
+
100
	/**
+
101
	 * A series of tests for method and class handling.
+
102
	 * When a caller presents certain methods with data concerning reflection,
+
103
	 * the Java classes you need to use for that are quite opaque, and don't
+
104
	 * offer much safety in any way.
+
105
	 * The solution therefore, is run some validation checks, but these take up
+
106
	 * a decent amount of space in terms of LoC.
+
107
	 * This method takes care of all that. Call this function whenever data
+
108
	 * needs to be validated.
+
109
	 * @param methodName The name of the method, as it is declared in object.
+
110
	 * @param object The class instance in where this method will be called.
+
111
	 * @return The method that could be derived from the supplied data, or null
+
112
	 * if that wasn't possible.
+
113
	 * @throws NullPointerException if either methodName or object are null
+
114
	 * pointers.
+
115
	 * @throws IllegalArgumentException if methodName is empty, or the method
+
116
	 * does not appear to be declared in the given object, or object is not a
+
117
	 * class.
+
118
	 */
+
119
	// All unchecked typecasts are safe, and the use of raw types is taken care
+
120
	// of.
+
121
	@SuppressWarnings({"unchecked","rawtypes"})
+
122
	private Method handleReflectionData(String methodName, Object object) {
+
123
		// Null pointer checking:
+
124
		if (methodName == null || object == null) {
+
125
			throw new NullPointerException("One or more of the given parameters are null pointers.");
+
126
		}
+
127
+
128
		// XXX: Some might say the next line should be in an else{} block. But
+
129
		// Scoping rules require that I'd then have to wrap the rest of the
+
130
		// method in the same else to use it.
+
131
		Class methodClass = object.getClass(); 
+
132
		if (methodName.equals("")) {
+
133
			throw new IllegalArgumentException("The given methodName was empty.");
+
134
		}
+
135
		Method method;
+
136
		try { // First: Look if there's a method without parameters.
+
137
			method = methodClass.getMethod(methodName, null);
+
138
		}
+
139
		catch (NoSuchMethodException exception) {
+
140
			try {
+
141
				// It's possible that the method requires an event parameter, so
+
142
				// check for that as well:
+
143
				Class<?>[] parameters = {java.awt.event.ActionEvent.class};
+
144
				method = methodClass.getMethod(methodName, parameters);
+
145
			}
+
146
			catch (NoSuchMethodException e) {
+
147
				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.");
+
148
			}
+
149
		}
+
150
		// At this stage, the given data has been validated, and we've been able
+
151
		// to retrieve the method itself.
+
152
		return method;
+
153
	}
+
154
+
155
	/**
+
156
	 * Creates a button in the GUI for interaction.
+
157
	 * This function offers a convenient way to create a button, that can be
+
158
	 * directly interacted with by the user. After creation, the button itself
+
159
	 * is returned to the caller, if he wishes to do something else with it.
+
160
	 * @param text The text that will be displayed in the button.
+
161
	 * @param action The action that will be returned to the action listener.
+
162
	 * @param methodName The name of the method that will be called when an
+
163
	 * action is triggered.
+
164
	 * @param triggerObject The object instance that contains the given method.
+
165
	 * This may only be a null pointer if triggerMethod is not an instance
+
166
	 * method.
+
167
	 * performed. This method may accept an ActionEvent parameter as its only
+
168
	 * parameter, or no parameters at all.
+
169
	 * @throws NullPointerException if triggerMethod is a null pointer, or
+
170
	 * the empty String was given.
+
171
	 * @throws IllegalArgumentException if triggerMethod has more than 1
+
172
	 * parameter, or the 1 required parameter is not of type ActionEvent.
+
173
	 * @return The button that was created.
+
174
	 * @see java.awt.event.ActionEvent
+
175
	 * @see java.lang.reflect.Method#invoke
+
176
	 */
+
177
	public JButton createButton(String text, String action, String methodName, Object triggerObject) {
+
178
		Method triggerMethod = this.handleReflectionData(methodName, triggerObject);
+
179
+
180
		// For starters, we first assert that the given parameters are valid:
+
181
		if (text == null) {
+
182
			text = "";
+
183
		}
+
184
		if (action == null) {
+
185
			action = "";
+
186
		}
+
187
		
+
188
		// When the method gets here, everything's been validated correctly.
+
189
		JButton button = new JButton(text);
+
190
		button.setActionCommand(action);
+
191
		button.addActionListener(
+
192
				new java.awt.event.ActionListener() {
+
193
					public void actionPerformed(java.awt.event.ActionEvent event) {
+
194
						try {
+
195
							triggerMethod.setAccessible(true);
+
196
							if (triggerMethod.getParameterTypes().length == 0) {
+
197
								// FIXME: Next line throws a warning?
+
198
								triggerMethod.invoke(triggerObject, null);
+
199
							}
+
200
							else {
+
201
								triggerMethod.invoke(triggerObject, new Object[]{event});
+
202
							}
+
203
						}
+
204
						catch (Exception useless) {
+
205
							/*
+
206
							 * XXX: Some info on why I don't just throw said
+
207
							 * Exception to the caller:
+
208
							 * Java has this awful language constraint, which
+
209
							 * forces every damn exception that isn't a subclass
+
210
							 * of RuntimeException, to be declared in the method
+
211
							 * declaration. This tends to infect all underlying
+
212
							 * methods as well, and all that for reasons I can't
+
213
							 * comprehend. In order to keep JSugar a simple and
+
214
							 * clean library, I'll rather just handle it here,
+
215
							 * and throw a RuntimeException with appropriate
+
216
							 * details.
+
217
							 */
+
218
							throw new IllegalArgumentException("triggerMethod is not accessible from this context.");
+
219
						}
+
220
					}
+
221
				});
+
222
		this.addComponent(button);
+
223
		return button;
+
224
	}
+
225
+
226
	/**
+
227
	 * Ask the user for input through a dialog box.
+
228
	 * This method presents the user with an input field, that can accept
+
229
	 * textual input. The method will return the given input after the user's
+
230
	 * clicked a button to send.
+
231
	 * @param text The text/question to be asked to the user.
+
232
	 * @return A String, equal to what the user entered.
+
233
	 * @throws NullPointerException if text is a null pointer.
+
234
	 */
+
235
	public String inputDialog(String text) {
+
236
		if (text == null) {
+
237
			throw new NullPointerException("The given text/question was a null pointer.");
+
238
		}
+
239
		return JOptionPane.showInputDialog(text);
+
240
	}
+
241
+
242
	/**
+
243
	 * Give the user a dialog box.
+
244
	 * This method can be used to provide a simple dialog to the user.
+
245
	 * This will show the user the given question, after which a boolean value
+
246
	 * is returned, holding the choice.
+
247
	 * @param text The text/question to be asked to the user.
+
248
	 * @return True if the user confirms, False if he denies.
+
249
	 * @throws NullPointerException if text is a null pointer.
+
250
	 */
+
251
	public boolean confirmDialog(String text) {
+
252
		if (text == null) {
+
253
			throw new NullPointerException("The given text/question was a null pointer.");
+
254
		}
+
255
		final int ACCEPTED = 0;
+
256
		//final int DENIED = 1; // Not used in the current context.
+
257
		int result = this.choiceDialog(text, new String[]{"Confirm", "Deny"});
+
258
		if (result == ACCEPTED) {
+
259
			return true;
+
260
		}
+
261
		else {
+
262
			return false;
+
263
		}
+
264
	}
+
265
+
266
	/**
+
267
	 * Give the user a choice dialog box.
+
268
	 * This method gives the user a simple dialog with predefined choices.
+
269
	 * These choices are to be provided by the caller in a simple array.
+
270
	 *
+
271
	 * Tip: This method works extremely well with arbitrary created choices.
+
272
	 * That is: if the outcome of the dialog is trivial (e.g. Only 1 choice),
+
273
	 * then that value is immediately returned.
+
274
	 * @param text The text/question to be asked to the user.
+
275
	 * @param choices An array of Strings, containing the choices the user can
+
276
	 * pick.
+
277
	 * @return The index value of the picked choice, or -1 if no choices were
+
278
	 * given.
+
279
	 * @throws NullPointerException if text is a null pointer.
+
280
	 */
+
281
	public int choiceDialog(String text, String[] choices) {
+
282
		if (text == null) {
+
283
			throw new NullPointerException("The given text/question was a null pointer.");
+
284
		}
+
285
		// First: handling the trivial cases:
+
286
		if (choices.length == 0) {
+
287
			return -1;
+
288
		}
+
289
		else if (choices.length == 1) {
+
290
			return 0;
+
291
		}
+
292
		int answer = JOptionPane.CLOSED_OPTION;
+
293
		// The dialog needs to be shown again until the user has made a possible
+
294
		// choice, i.e. Chickening out using the close button is not possible
+
295
		// (Because that returns CLOSED_OPTION).
+
296
		while (answer == JOptionPane.CLOSED_OPTION) {
+
297
				JOptionPane.showOptionDialog(
+
298
					null, // The parent component. May become the panel?
+
299
					text, // The text/question to describe the goal
+
300
					"Dialog", // The text in the title bar
+
301
					JOptionPane.DEFAULT_OPTION, // The kind of available options
+
302
					JOptionPane.QUESTION_MESSAGE, // The type of message
+
303
					null, // The icon to show
+
304
					choices, // The possible choices
+
305
					choices[0] // The standard choice
+
306
					);
+
307
		}
+
308
		return answer;
+
309
	}
+
310
		
+
311
+
312
	/**
+
313
	 * Creates a label in the GUI for interaction.
+
314
	 * This function offers a convenient way to create a label, that can be
+
315
	 * directly interacted with by the user. After creation, the label itself
+
316
	 * is returned to the caller, if he wishes to do something else with it.
+
317
	 * @param text The text that will be displayed in the label.
+
318
	 * @return The label that was created.
+
319
	 */
+
320
	public JLabel createLabel(String text) {
+
321
		JLabel label = new JLabel(text);
+
322
		this.addComponent(label);
+
323
		return label;
+
324
	}
+
325
+
326
	/**
+
327
	 * Adds a checkbox to the window.
+
328
	 * By providing a String, you can use this method to easily
+
329
	 * create a checkbox, and add it to the window. 
+
330
	 * @param text The text to put next to the checkbox.
+
331
	 * @return The checkbox that was created and added to the GUI.
+
332
	 */
+
333
	public JCheckBox createCheckbox(String text) {
+
334
		JCheckBox checkbox = new JCheckBox(text);
+
335
		this.addComponent(checkbox);
+
336
		return checkbox;
+
337
	}
+
338
+
339
	/**
+
340
	 * Adds radio buttons to the window.
+
341
	 * Given a list of Strings, this method will create the same amount of radio
+
342
	 * buttons.
+
343
	 *
+
344
	 * The radio buttons will silently be grouped in a ButtonGroup object,
+
345
	 * making them automatically disable each other, so only 1 radio button can
+
346
	 * be enabled. This ButtonGroup is immutable.
+
347
	 *
+
348
	 * If you need a mutable ButtonGroup, create your own, and use the 
+
349
	 * {@link #addComponent} method to add the radio buttons manually.
+
350
	 * @param text An array of Strings. The length of the array will determine
+
351
	 * the amount of radio buttons that will be created.
+
352
	 * @return An array of radio buttons, in the same order as text.
+
353
	 */
+
354
	public JRadioButton[] createRadioButtons(String text[]) {
+
355
		JRadioButton[] radioButtons = new JRadioButton[text.length];
+
356
		ButtonGroup buttonGroup = new ButtonGroup();
+
357
		for (int i=0; i<radioButtons.length; i++) {
+
358
			radioButtons[i] = new JRadioButton(text[i]);
+
359
			//radioButtons[i].setText(text[i]);
+
360
			buttonGroup.add(radioButtons[i]);
+
361
			this.addComponent(radioButtons[i]);
+
362
		}
+
363
+
364
		assert radioButtons.length == buttonGroup.getButtonCount() : "The amount of radio buttons ("+ radioButtons.length +") differs from the amount of buttons in buttonGroup ("+ buttonGroup.getButtonCount() +").";
+
365
		return radioButtons;
+
366
	}
+
367
+
368
	public JTextField createTextField(String text) {
+
369
		JTextField textField = new JTextField(text);
+
370
		this.addComponent(textField);
+
371
		return textField;
+
372
	}
+
373
+
374
	/**
+
375
	 * Adds a number spinner component to the GUI.
+
376
	 * This method allows the caller to create a so-called "spinner component"
+
377
	 * to the window. This spinner is an input box, in which only integers can
+
378
	 * be put.
+
379
	 *
+
380
	 * The caller can set a range, a start value, and a step size.
+
381
	 *
+
382
	 * The spinner created with this method may modify itself based on the
+
383
	 * parameters.
+
384
	 * For example: If the minimum and maximum value are equal, the spinner will
+
385
	 * be disabled.
+
386
	 *
+
387
	 * @param minimum The minimum value that can be selected.
+
388
	 * @param maximum The maximum value that can be selected.
+
389
	 * @param start The value that will initially be shown in the component.
+
390
	 * @param stepSize The step size when the user increases/decreases the
+
391
	 * value.
+
392
	 * @throws IllegalArgumentException if minimum is larger than maximum, 
+
393
	 * start is not in the range of the selectable values, or stepsize is not a
+
394
	 * natural number.
+
395
	 * @return The JSpinner that was added to the window.
+
396
	 */
+
397
	public JSpinner createSpinner(int minimum, int maximum, int start, int stepSize) {
+
398
		// As usual, we begin with checking the contract:
+
399
		if(minimum > maximum) {
+
400
			throw new IllegalArgumentException("The minimum value ("+ minimum +") was larger than the maximum value ("+ maximum +")");
+
401
		}
+
402
		// The "start ∉ [minimum, maximum]" is done by the SpinnerNumberModel
+
403
		// constructor, which will be constructed later.
+
404
		if(stepSize <= 0) { // stepSize ∉ ℕ¹ (In Belgium: ℕ₀)
+
405
			throw new IllegalArgumentException("The stepSize ("+ stepSize +") is not a natural number (excluding 0).");
+
406
		}
+
407
		// If the contract is valid, we can begin:
+
408
		/*
+
409
		 * I'd like to interject here, because this is a nice example of why
+
410
		 * JSugar was a good idea:
+
411
		 * If you want a spinner, you'll typically want an integer spinner. But
+
412
		 * in Swing, when you create a JSpinner, it creates a JSpinner, with a
+
413
		 * predefined 'SpinnerNumberModel' attached to it.
+
414
		 * It's this model you then have to extract from the created spinner, on
+
415
		 * which you need to apply the configuration.
+
416
		 * What you actually have to do, is create a SpinnerNumberModel
+
417
		 * yourself, put your settings on that, and then, create a JSpinner to
+
418
		 * which you give that SpinnerNumberModel.
+
419
		 * In essence: The entire Java framework is shit.
+
420
		 */
+
421
		SpinnerNumberModel spinnerSettings = new SpinnerNumberModel(
+
422
				start,
+
423
				minimum,
+
424
				maximum,
+
425
				stepSize
+
426
				);
+
427
		JSpinner spinner = new JSpinner(spinnerSettings);
+
428
		if(minimum == maximum) { // Trivial value is set already, --> disable.
+
429
			spinner.setEnabled(false);
+
430
		}
+
431
		this.addComponent(spinner);
+
432
		return spinner;
+
433
	}
+
434
+
435
	/**
+
436
	 * Adds a number spinner component to the GUI.
+
437
	 * This method allows the caller to create a so-called "spinner component"
+
438
	 * to the window. This spinner is an input box, in which only integers can
+
439
	 * be put.
+
440
	 * 
+
441
	 * Tip: This method is a convenience method, and works extremely well with
+
442
	 * arbitrary data.
+
443
	 * For example: The start value is automatically set to the minimal possible
+
444
	 * value, and the step size defaults to 1.
+
445
	 * If the minimum and maximum are equal, the component will be disabled, and
+
446
	 * thus, be locked on the only (trivially) possible value.
+
447
	 * If minimum is larger than maximum, the method will notify you of this,
+
448
	 * but also swap the values. So you can rest assured that the spinner will
+
449
	 * handle errors, but also, inform you about it.
+
450
	 * @param minimum The minimum value that can be selected.
+
451
	 * @param maximum The maximum value that can be selected.
+
452
	 * @return The JSpinner component that was added to the window.
+
453
	 */
+
454
	public JSpinner createSpinner(int minimum, int maximum) {
+
455
		// The disabling of equal values is done in the full createSpinner(), so
+
456
		// this is merely switching values if they need to be swapped.
+
457
		if(minimum > maximum) {
+
458
			System.err.println("minimum ("+ minimum +") was larger than maximum ("+ maximum +").");
+
459
			// FIXME: Consider whether it's appropriate to print a stacktrace
+
460
			// here, which may be convenient for debugging.
+
461
			
+
462
			// XXX: I know you don't need the help variable when swapping
+
463
			// integers, because you can also do basic arithmetics. Change it if
+
464
			// it causes too much eye burn for you.
+
465
			int swapValue = minimum;
+
466
			minimum = maximum;
+
467
			maximum = swapValue;
+
468
		}
+
469
+
470
		// Yeah, these 2 variables make you cringe huh, performance addicts?
+
471
		// Drown me in the tears of your useless performance-related opinions.
+
472
		int startValue = minimum;
+
473
		int stepSize = 1;
+
474
		return this.createSpinner(minimum, maximum, startValue, stepSize);
+
475
	}
+
476
+
477
	/**
+
478
	 * Adds a combobox to the GUI.
+
479
	 * Allows the caller to create a combobox by providing the values that
+
480
	 * should be put in it.
+
481
	 *
+
482
	 * This method can only be used for String values. If that is not what you
+
483
	 * need, consider creating your own combobox and adding it manually. Or, if
+
484
	 * you need a combobox for integers, consider {@link #createSpinner}.
+
485
	 *
+
486
	 * WARNING: {@link JComboBox#getSelectedItem} returns an object, not a
+
487
	 * String. You need to manually typecast this. This is a constraint of the
+
488
	 * Swing framework.
+
489
	 * @param items An array of Strings that will be put in the combobox.
+
490
	 * @throws NullPointerException if one of the values in items is a null
+
491
	 * pointer.
+
492
	 * @throws IllegalArgumentException if items is empty.
+
493
	 * @return The JCombobox that was added to the window.
+
494
	 */
+
495
	public JComboBox<String> addComboBox(String[] items) {
+
496
		// Contract validation:
+
497
		if(items.length == 0) {
+
498
			throw new IllegalArgumentException("The given array of items was empty.");
+
499
		}
+
500
		for(String item : items) {
+
501
			if(item == null) {
+
502
				throw new NullPointerException("One of the given Strings is a null pointer.");
+
503
			}
+
504
		}
+
505
		// Contract validated, create the component:
+
506
		JComboBox<String> comboBox = new JComboBox<String>(items);
+
507
		comboBox.setSelectedIndex(0);
+
508
		if(comboBox.getItemCount() == 1) { // Trivial selection
+
509
			comboBox.setEnabled(false);
+
510
		}
+
511
		this.addComponent(comboBox);
+
512
		return comboBox;
+
513
	}
+
514
+
515
	/**
+
516
	 * Creates a list of the given data, and adds it to the GUI.
+
517
	 * This will create a JList component, containing the given data. 
+
518
	 * To jar up your memory: A list in this context, is a component in which
+
519
	 * data of the same type is printed out. The user of said list, can then
+
520
	 * select a subset of these items.
+
521
	 *
+
522
	 * @see JList for a collection of possible operations.
+
523
	 * @param items The String items that will be put in the list.
+
524
	 * @throws NullPointerException if one of the values in items is a null
+
525
	 * pointer.
+
526
	 * @throws IllegalArgumentException if items is empty.
+
527
	 * @return A JList component, that was a added to the GUI.
+
528
	 */
+
529
	public JList createList(String[] items) {
+
530
		// Contract validation:
+
531
		if(items.length == 0) {
+
532
			throw new IllegalArgumentException("The given array of items was empty.");
+
533
		}
+
534
		for(String item : items) {
+
535
			if(item == null) {
+
536
				throw new NullPointerException("One of the given Strings is a null pointer.");
+
537
			}
+
538
		}
+
539
		// Contract validated, create the component:
+
540
		JList list = new JList(items);
+
541
		this.addComponent(list);
+
542
		return list;
+
543
	}
+
544
+
545
	/**
+
546
	 * Creates a table of the given data, and adds it to the GUI.
+
547
	 * This method allows you to create a table with sorting functionality in
+
548
	 * the GUI.
+
549
	 * This method relies on implications, deducted from the given data. That
+
550
	 * is, the length of the rows and columns is calculated by the longest
+
551
	 * length it can find in the nested array.
+
552
	 * To change the data, take a look at the JTable documentation.
+
553
	 *
+
554
	 * @see JTable for a collection of possible operations.
+
555
	 * @param items The String items that will be put in the list.
+
556
	 * @throws NullPointerException if one of the values in items is a null
+
557
	 * pointer.
+
558
	 * @throws IllegalArgumentException if items is empty, or the amount of
+
559
	 * column names does not correspond with the given amount of items.
+
560
	 * @return A JTable component, that was a added to the GUI.
+
561
	 */
+
562
	public JTable createTable(String[] columns, String[][] items) {
+
563
		// Contract validation:
+
564
		if(items.length == 0) {
+
565
			throw new IllegalArgumentException("The given array of items was empty.");
+
566
		}
+
567
		if(columns.length != items[0].length) {
+
568
			throw new IllegalArgumentException("The amount of columns does not correspond to the given amount of items.");
+
569
		}
+
570
		for(int i=0; i<items.length; i++) {
+
571
			if(items[i] == null) {
+
572
				throw new NullPointerException("One of the given Strings is a null pointer.");
+
573
			}
+
574
		}
+
575
		// Contract validated, create the component:
+
576
		// Deducting max length:
+
577
		int columnCount = columns.length;
+
578
		int rowCount = items.length;
+
579
		
+
580
		JTable table = new JTable(items, columns);
+
581
		this.addComponent(table);
+
582
		return table;
+
583
	}
+
584
+
585
	/**
+
586
	 * Adds the given component to the GUI.
+
587
	 * This method allows its caller to give a pre-made component, so that it
+
588
	 * can be added to the GUI. Even though its main use is for the Window class
+
589
	 * itself, the user of JSugar can also use it to create components himself,
+
590
	 * and then add them. As such, this method doesn't provide parameters for
+
591
	 * reflection/action triggering purposes.
+
592
	 * @param component The component to be added to the window.
+
593
	 * @throws NullPointerException if the given component is a null pointer.
+
594
	 */
+
595
	public void addComponent(JComponent component) {
+
596
		int originalSize = this.panel.getComponentCount();
+
597
		this.panel.add(component); // Throws the exception if null.
+
598
		this.updateWindow();
+
599
+
600
		assert originalSize == this.panel.getComponentCount()-1 : "A component was supposed to be added to the window, but the total amount of components was unchanged after the addition.";
+
601
	}
+
602
+
603
	/**
+
604
	 * Removes the given component from the GUI.
+
605
	 * This method allows its caller to remove a component from the GUI.
+
606
	 * @param component The component to be removed.
+
607
	 * @throws NoSuchElementException if the given component does not exist in
+
608
	 * the GUI.
+
609
	 * @throws NullPointerException if the given component is a null pointer.
+
610
	 */
+
611
	public void removeComponent(JComponent component) {
+
612
		int originalSize = this.panel.getComponentCount();
+
613
		this.panel.remove(component);
+
614
		int newSize = this.panel.getComponentCount();
+
615
		if (originalSize != newSize+1) {
+
616
			throw new NoSuchElementException("The given component does not exist in the GUI.");
+
617
		}
+
618
		this.updateWindow();
+
619
	}
+
620
	/**
+
621
	 * Prompts the user with a file chooser dialog.
+
622
	 * By calling this method, the user will be presented with a file chooser
+
623
	 * dialog, out of which a single file can be selected. If the selected file
+
624
	 * exists, a File object is returned, a null pointer if the user cancelled.
+
625
	 * @return A File object representing the file the user selected, or null
+
626
	 * otherwise.
+
627
	 */
+
628
	public File openFileChooserDialog() {
+
629
		JFileChooser fileDialog = new JFileChooser();
+
630
		fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
+
631
+
632
		int userResponse = fileDialog.showOpenDialog(this.panel);
+
633
		if(userResponse == JFileChooser.APPROVE_OPTION) {
+
634
			return fileDialog.getSelectedFile();
+
635
		}
+
636
		else {
+
637
			return null;
+
638
		}
+
639
	}
+
640
}
+
641