OOP2

I've been experimenting to see if my JSugar is responsible for the crashing of Challenge 4. Turns out it's not, and in fact, is a result from Java's verbosity and forced usage of objects.

Author
Vngngdn
Date
Oct. 21, 2016, 3:58 p.m.
Hash
b3673cbdc53cb5e72195a10ccdfaefe7f04d71b8
Parent
baa12ac6f760288ff5a02ddd7ae556840a1447c4
Modified files
Challenge 4/Controller.java
Challenge 4/LendingController.java
Challenge 4/Window.java

Challenge 4/Controller.java

1 addition and 0 deletions.

View changes Hide changes
1
1
import java.util.ArrayList;
2
2
import javax.swing.JCheckBox;
3
3
import javax.swing.JComponent;
4
4
import javax.swing.JButton;
5
5
/**
6
6
 * @author Maarten Vangeneugden - 1438256
7
7
 */
8
8
public class Controller {
9
9
10
10
	private List<Article> articles;
11
11
	private List<Client> clients;
12
12
	private List<Penalty> penalties;
13
13
	private Window window;
14
14
	private List<JComponent> mainScreenComponents;
15
15
16
16
	public Controller(List<Article> articles, List<Client> clients) { 
17
17
		this.articles = articles;
18
18
		this.clients = clients;
19
19
		this.penalties = new ArrayList();
20
20
		this.mainScreenComponents = new ArrayList();
21
21
		this.createMainScreen();
22
22
		
23
23
	}
24
24
25
25
	/**
26
26
	 * Creates the main screen, out of which the user can choose the different
27
27
	 * things to do.
28
28
	 */
29
29
	private void createMainScreen() {
30
30
		Window gui = new Window("Bibliotheek");
31
31
		this.mainScreenComponents.add(gui.createButton(
+
32
		this.mainScreenComponents.add(gui.createButton(
32
33
				"Leen artikels uit",
33
34
				"",
34
35
				"createLendingScreen",
35
36
				this
36
37
				));
37
38
		this.mainScreenComponents.add(gui.createButton(
38
39
				"Beheer wachtlijsten",
39
40
				"",
40
41
				"createQueueScreen",
41
42
				this
42
43
				));
43
44
		this.mainScreenComponents.add(gui.createButton(
44
45
				"Schrijf boete uit",
45
46
				"",
46
47
				"createPenaltyScreen",
47
48
				this
48
49
				));
49
50
	}
50
51
51
52
	public void createQueueScreen() {
52
53
	}
53
54
54
55
	/**
55
56
	 * Swaps the visibility state of the main screen buttons.
56
57
	 */
57
58
	private void swapMainButtonsState() {
58
59
		for(JComponent component: this.mainScreenComponents) {
59
60
			component.setVisible(!component.isVisible());
60
61
		}
61
62
	}
62
63
63
64
	public void createLendingScreen() {
64
65
		//this.swapMainButtonsState();
65
66
		LendingController lendingController = new LendingController(this.articles, this.clients, this.window);
66
67
		//this.swapMainButtonsState();
67
68
68
69
		Window lendingScreen = new Window("Artikels uitlenen");
69
70
		lendingScreen.addComboBox(this.getClients().toArray(new String[0]));
70
71
		List<Article> availableArticles = new ArrayList();
71
72
		for(Article article: this.articles) {
72
73
			if(article.getLendedSince() == null) { // Available article
73
74
				availableArticles.add(article);
74
75
			}
75
76
		}
76
77
77
78
78
79
	}
79
80
80
81
	public void createPenaltyScreen() {
81
82
		//this.swapMainButtonsState();
82
83
		//this.window.createButton("Sluiten", "", "closeWindow", this);
83
84
		PenaltyController penaltyController = new PenaltyController(this.penalties, this.window);
84
85
		//JButton closeButton = this.window.createButton("Sluiten", "", "removePenalties", penaltyController);
85
86
		//this.swapMainButtonsState();
86
87
	}
87
88
88
89
89
90
90
91
91
92
92
93
93
94
94
95
		
95
96
96
97
	public void setArticles(List<Article> articles) {
97
98
		this.articles = articles;
98
99
	}
99
100
100
101
	public List<Article> getArticles() {	
101
102
		return articles;
102
103
	}
103
104
104
105
	public void setClients(List<Client> clients) {
105
106
		this.clients = clients;
106
107
	}
107
108
108
109
	public List<Client> getClients() {	
109
110
		return clients;
110
111
	}
111
112
112
113
	/**
113
114
	 * Translates a given date to an array of 3 integers.
114
115
	 * @param stringDate The date to translate.
115
116
	 * @pre stringDate must be of form DD-MM-YYYY
116
117
	 */
117
118
	public static int[] getDate(String stringDate) {
118
119
		int[] date = new int[3];
119
120
		String[] dateParts = stringDate.split("-");
120
121
		for(int i=0; i<date.length; i++) {
121
122
			date[i] = Integer.parseInt(dateParts[i]);
122
123
		}
123
124
		return date;
124
125
	}
125
126
}
126
127

Challenge 4/LendingController.java

1 addition and 0 deletions.

View changes Hide changes
1
1
import java.util.ArrayList;
2
2
import javax.swing.JCheckBox;
3
3
import javax.swing.JButton;
4
4
import javax.swing.JComponent;
5
5
/**
6
6
 * @author Maarten Vangeneugden - 1438256
7
7
 */
8
8
public class LendingController {
9
9
10
10
	private List<Article> articles;
11
11
	private List<Client> clients;
12
12
	private Window window;
13
13
	private List<JComponent> components;
14
14
15
15
	public LendingController(List<Article> articles, List<Client> clients, Window window) { 
16
16
		this.articles = articles;
17
17
		this.clients = clients;
18
18
		this.window = window;
19
19
		//this.components.add(this.window.createButton("Sluiten", "", "closeWindow", this));
+
20
		//this.components.add(this.window.createButton("Sluiten", "", "closeWindow", this));
20
21
		this.createWindow();
21
22
	}
22
23
23
24
	private void createWindow() {
24
25
		String[] clientNames = new String[this.clients.size()];
25
26
		for(int i=0; i<clientNames.length; i++) {
26
27
			clientNames[i] = this.clients.get(i).getName();
27
28
		}
28
29
29
30
		this.components.add(this.window.addComboBox(clientNames));
30
31
	}
31
32
		
32
33
	public void closeWindow() {
33
34
34
35
	}
35
36
36
37
	public void setArticles(List<Article> articles) {
37
38
		this.articles = articles;
38
39
	}
39
40
40
41
	public List<Article> getArticles() {	
41
42
		return articles;
42
43
	}
43
44
44
45
	public void setClients(List<Client> clients) {
45
46
		this.clients = clients;
46
47
	}
47
48
48
49
	public List<Client> getClients() {	
49
50
		return clients;
50
51
	}
51
52
52
53
	public void setWindow(Window window) {
53
54
		this.window = window;
54
55
	}
55
56
56
57
	public Window getWindow() {	
57
58
		return window;
58
59
	}
59
60
60
61
}
61
62

Challenge 4/Window.java

14 additions and 3 deletions.

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