Enrol.java
// IM264 Coursework - Course Enrolment System
// Eamonn Martin - BSc (hons) Computing - ID: 96/D59682
// package im264;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.sql.*;
// -------------------------
// General-Purpose Classes
// -------------------------
// Useful static methods
class Lib {
// Return the frame containing this EnrolPanel (or null)
public static Frame parentFrame(Component c) {
while (c.getParent()!=null) c = c.getParent();
return c instanceof Frame ? (Frame)c : null;
}
}
// A labelled text-field component
class LabelledTextField extends Panel {
private TextField label, text;
// Constructors
public LabelledTextField(String ltext, int width) { this(ltext, width, true); }
public LabelledTextField(String ltext, int width, boolean isEditable) {
setLayout(new BorderLayout());
add(label = new TextField(ltext, width), "West");
label.setEnabled(false);
add(text = new TextField(), "Center");
text.setEditable(isEditable);
text.addFocusListener(new FocusListener() { // Tidy-up Explorer applet!
public void focusGained(FocusEvent e) {
text.setCaretPosition(text.getText().length());
}
public void focusLost(FocusEvent e) {
text.setCaretPosition(0);
}
});
}
// Query
public String getLabel() { return label.getText(); }
public String getText() { return text.getText(); }
// Update
public void setLabel(String label) { this.label.setText(label); }
public void setText(String text) { this.text.setText(text); }
}
// A panel of labelled text-fields
class TextForm extends Panel {
private LabelledTextField[] fields;
// Constructors
public TextForm(String[] labels, int width) {
this(labels, width, new GridLayout(labels.length, 1));
}
public TextForm(String[] labels, int width, LayoutManager mngr) {
setLayout(mngr);
fields = new LabelledTextField[labels.length];
for (int i=0; i < labels.length; ++i)
add(fields[i] = new LabelledTextField(labels[i], width));
}
// Return the array of LabelledTextField components
public LabelledTextField[] getFields() { return fields; }
// Return the seperator-string to use between labels and text in getText()
public String getSep() { return " "; }
// Return the terminator-string to end a field in getText()
public String getTerm() { return "\n"; }
// Return all text from the LabelledTextField components on this panel
public String getText() { return getText(null, null, true); }
public String getText(String sep, String term, boolean labels) {
LabelledTextField f;
String s = "";
if (term==null) term = getTerm();
if (sep==null) sep = getSep();
for (int i=0; i < fields.length; ++i) {
f = fields[i];
if (labels) s += f.getLabel() + sep;
s += f.getText() + term;
}
return s;
}
}
// A panel of check-boxes
class CheckboxPanel extends Panel {
private CheckboxGroup group;
// Constructors
public CheckboxPanel() { this(new GridLayout()); }
public CheckboxPanel(LayoutManager manager) {
group = new CheckboxGroup();
setLayout(manager);
}
// Add a checkbox with the specified label, state and event-handler
public void addCheckbox(String label, ItemListener listener, boolean state) {
Checkbox checkbox = new Checkbox(label, group, state);
checkbox.addItemListener(listener);
add(checkbox);
}
}
// ------------------------------
// Application-Specific Classes
// ------------------------------
// Manage interactions with the enrolment database
class EnrolDatabase {
// Database access strings
private static final String
DRIVER = "sun.jdbc.odbc.JdbcOdbcDriver",
PROTOCOL = "jdbc:odbc:",
USERNAME = null,
PASSWORD = null;
// Table ID values
static final int
COURSE = 0,
STUDENT = 1,
CHEQUE = 2,
CREDITCARD = 3,
PURCHASEORDER = 4;
// Database definitions
private static final String
DATABASE = "Enrol",
TABLE_COURSE = "Course",
COLUMNS_COURSE = "code, title, maxEnrol",
TABLE_STUDENT = "Student",
STUDENT_COURSE = "course",
COLUMNS_STUDENT = "id, "+STUDENT_COURSE+", name, tel, fax, email",
STUDENT_PAID = "paid",
STUDENT_INFO = "name, attended, " + STUDENT_PAID,
TABLE_CHEQUE = "Cheque",
COLUMNS_CHEQUE = "id, number",
TABLE_CREDITCARD = "CreditCard",
COLUMNS_CREDITCARD = "id, number, expiry, name",
TABLE_PURCHASEORDER = "PurchaseOrder",
COLUMNS_PURCHASEORDER = "id, number, company, name, tel";
// Column indices for the Course table
private static final int
COL_CODE = 1,
COL_TITLE = 2,
COL_MAXENROL = 3;
// Column indices for the Student table
private static final int
COL_NAME = 1,
COL_ATTENDED = 2,
COL_PAID = 3;
// Attempt to open a connection to the database
private static Connection getConnection() throws Exception {
Class.forName(DRIVER); // Load driver
String url = PROTOCOL + DATABASE; // Create URL
return DriverManager.getConnection(url, USERNAME, PASSWORD);
}
// Query the database to determine the number of students enroled for a course
private static int getNumEnroled(String code, Connection c) {
try {
Statement s = c.createStatement();
String q = "SELECT COUNT(*) FROM " + TABLE_STUDENT +
" WHERE " + STUDENT_COURSE + "='" + code + "';";
ResultSet r = s.executeQuery(q);
int n = r.next() ? r.getInt(1) : -1;
s.close();
return n;
} catch (SQLException e) {}
return -1;
}
// Retrieve course information from the database (*IMPLEMENTS TEST-MODE*)
static CourseData[] getCourses() {
try {
Connection c = getConnection(); // Connect
Statement s = c.createStatement(); // Create statement
String q = "SELECT "+COLUMNS_COURSE+" FROM "+TABLE_COURSE+";";
ResultSet r = s.executeQuery(q); // Execute query
CourseData data = new CourseData(), d = data;
int num = 0;
while (r.next()) {
d = (d.next = new CourseData());
d.code = r.getString(COL_CODE); // Course code
d.title = r.getString(COL_TITLE); // Course title
d.maxEnrol = r.getInt(COL_MAXENROL); // Enrolment limit
d.numEnrol = getNumEnroled(d.code, c); // Enrolment count
++num;
}
CourseData[] courses = new CourseData[num];
for (int i=0; i < num; ++i) courses[i] = (data = data.next);
return courses;
} catch(Exception e) { System.out.println(e); }
if (Enrol.READTEST) return getDummyCourses(); // Test-mode?
return null; // Failed
}
// Update a database table with a set of values (*IMPLEMENTS TEST-MODE*)
static boolean update(int tableID, String values) {
String name = null, cols = null;
switch (tableID) {
case STUDENT:
name = TABLE_STUDENT;
cols = COLUMNS_STUDENT;
break;
case CHEQUE:
name = TABLE_CHEQUE;
cols = COLUMNS_CHEQUE;
break;
case CREDITCARD:
name = TABLE_CREDITCARD;
cols = COLUMNS_CREDITCARD;
break;
case PURCHASEORDER:
name = TABLE_PURCHASEORDER;
cols = COLUMNS_PURCHASEORDER;
break;
default: return false;
}
String table = name + " (" + cols + ")";
try {
Connection c = getConnection(); // Connect
Statement s = c.createStatement(); // Create statement
String u = "INSERT INTO " + table + " VALUES (" + values + ");";
s.executeUpdate(u); // Execute update
return true; // Success
} catch(Exception e) { System.out.println(e); } // Error?
if (Enrol.WRITETEST) return true; // Test-mode?
return false;
}
// Attempt to retrieve student information from the database
private static StudentData[] getStudentInfo(String query) {
try {
Connection c = getConnection(); // Connect
Statement s = c.createStatement(); // Create statement
ResultSet r = s.executeQuery(query); // Execute query
StudentData data = new StudentData(), d = data;
int num = 0;
while (r.next()) {
d = (d.next = new StudentData());
d.name = r.getString(COL_NAME); // Student name
d.attended = r.getInt(COL_ATTENDED)!=0; // Attendance
d.paid = r.getInt(COL_PAID)!=0; // Payment
++num;
}
StudentData[] students = new StudentData[num];
for (int i=0; i < num; ++i) students[i] = (data = data.next);
return students;
} catch(Exception e) { System.out.println(e); }
return null; // Failed
}
// Return students enroled on a specific course (*IMPLEMENTS TEST-MODE*)
static StudentData[] getStudents(CourseData course) {
String s = "SELECT " + STUDENT_INFO + " FROM " + TABLE_STUDENT +
" WHERE " + STUDENT_COURSE + "='" + course.code + "';";
StudentData[] info = getStudentInfo(s); // Execute query
if (info!=null) return info; // Query ok?
return Enrol.READTEST ? getDummyStudents() : null; // Test-mode?
}
// Return students with unpaid balances (*IMPLEMENTS TEST-MODE*)
static StudentData[] getUnpaid() {
String s = "SELECT " + STUDENT_INFO + " FROM " + TABLE_STUDENT +
" WHERE " + STUDENT_PAID + "=0;";
StudentData[] info = getStudentInfo(s); // Execute query
if (info!=null) return info; // Query ok?
return Enrol.READTEST ? getDummyUnpaid() : null; // Test-mode?
}
// The following methods are used to implement an offline test-mode
// TEST-MODE - Return a dummy course-list
private static CourseData[] getDummyCourses() {
CourseData
c1 = new CourseData("CODEG", "BSc Computer Science", 60, 40),
c2 = new CourseData("CUDEG", "BSc Computing", 60, 35),
courses[] = { c1, c2 };
return courses;
}
// TEST-MODE - Return a dummy student-list
private static StudentData[] getDummyStudents() {
StudentData
s1 = new StudentData("Brian Kernighan", true, true),
s2 = new StudentData("Bill Gates", false, true),
s3 = new StudentData("Steve Jobbs", true, false),
s4 = new StudentData("Linus Torvalds", false, false),
students[] = { s1, s2, s3, s4 };
return students;
}
// TEST-MODE - Return a dummy unpaid-list
private static StudentData[] getDummyUnpaid() {
StudentData
s1 = new StudentData("Steve Jobbs", true, false),
s2 = new StudentData("Linus Torvalds", false, false),
students[] = { s1, s2 };
return students;
}
}
// Class used to return course information from the database
class CourseData {
String code, title;
int maxEnrol, numEnrol;
CourseData next;
// Constructors
CourseData() {}
CourseData(String code, String title, int maxEnrol, int numEnrol) {
this.code = code;
this.title = title;
this.maxEnrol = maxEnrol;
this.numEnrol = numEnrol;
}
}
// Class used to return student information from the database
class StudentData {
String name;
boolean attended, paid;
StudentData next;
// Constructors
StudentData() {}
StudentData(String name, boolean attended, boolean paid) {
this.name = name;
this.attended = attended;
this.paid = paid;
}
}
// ----------------------------------------------------------------------------------
// The following component-classes are used to construct the DetailsPanel component
// ----------------------------------------------------------------------------------
// Course information component
class CourseInfo extends LabelledTextField {
CourseInfo() { super("Course:", 14, false); }
public String getText() { return getLabel() + " " + super.getText() + "\n"; }
}
// Student information component
class StudentInfo extends TextForm {
private static final String[] labels = { "Name:", "Phone:", "Fax:", "E-mail:" };
// Constructor
StudentInfo() { super(labels, 14); }
// Update the student table in the database
boolean updateTable(CourseData course) {
String v = getText(null, "', '", false); // Get student input
v = v.substring(0, v.length()-3); // Remove last comma etc
v = (course.numEnrol+1) + ", '" + course.code + "', '" + v;
return EnrolDatabase.update(EnrolDatabase.STUDENT, v);
}
}
// Payment Methods
// Interface implemented by payment-method classes
interface PaymentMethod {
String getMethod(); // Return a string describing the payment-method
String getText(); // Return the text of the input form (from TextForm)
}
// Generic super-class for payment-method TextForm components
class PaymentForm extends TextForm {
private int table;
// Constructor
PaymentForm(int table, String[] labels, int width) {
super(labels, width);
this.table = table;
}
// Update the database with the text in this PaymentForm component
boolean updateTable(CourseData course) {
String v = getText(null, "', '", false); // Format text values
v = v.substring(0, v.length()-3); // Remove last comma etc
v = (course.numEnrol+1) + ", '" + v; // Add new student ID
return EnrolDatabase.update(table, v); // Update database
}
}
// Cheque information
class ChequeInfo extends PaymentForm implements PaymentMethod {
private static final String[] labels = { "Number:" };
ChequeInfo() { super(EnrolDatabase.CHEQUE, labels, 14); }
public String getMethod() { return "Cheque"; }
}
// Credit card information
class CreditCardInfo extends PaymentForm implements PaymentMethod {
private static final String[] labels = { "Number:", "Expiry:", "Name:" };
CreditCardInfo() { super(EnrolDatabase.CREDITCARD, labels, 14); }
public String getMethod() { return "Credit Card"; }
}
// Purchase Order information
class PurchaseOrderInfo extends PaymentForm implements PaymentMethod {
private static final String[] labels =
{ "Company:", "Order No:", "Contact Name:", "Contact Tel:" };
PurchaseOrderInfo() { super(EnrolDatabase.PURCHASEORDER, labels, 14); }
public String getMethod() { return "Purchase Order"; }
}
// Payment information component
class PaymentInfo extends Panel implements ItemListener {
private static Class[] methodTypes = {
ChequeInfo.class, CreditCardInfo.class, PurchaseOrderInfo.class
};
private PaymentMethod methods[], method;
private CheckboxPanel selector;
// Constructors
PaymentInfo() { this(methodTypes); }
PaymentInfo(Class[] methodTypes) {
methods = new PaymentMethod[methodTypes.length];
selector = new CheckboxPanel();
boolean state = true;
for (int i=0; i < methods.length; ++i) try {
methods[i] = (PaymentMethod)methodTypes[i].newInstance();
selector.addCheckbox(methods[i].getMethod(), this, state);
state = false;
} catch (Exception e) {}
setLayout(new BorderLayout());
add(selector, "North");
add((Component)(method = methods[0]), "Center");
}
// Change payment method
public void itemStateChanged(ItemEvent e) {
String label = (String)e.getItem();
remove((Component)method);
for (int i=0; i < methods.length; ++i)
if (label.equals(methods[i].getMethod()))
add((Component)(method = methods[i]), "Center");
}
// Return payment details
public String getText() {
return "Method: " + method.getMethod() + "\n" + method.getText();
}
// Update the database table for the current payment method
boolean updateTable(CourseData course) {
return ((PaymentForm)method).updateTable(course);
}
}
// ------------------------------------------------------------------------------------
// The following three components are the display-panels for the EnrolPanel component
// ------------------------------------------------------------------------------------
// Course-list panel
class CoursePanel extends Panel {
CourseData[] courses = null;
private List course;
// Constructors
CoursePanel(ActionListener listener, String enrol) { this(listener, enrol, null); }
CoursePanel(ActionListener listener, String cmd1, String cmd2) {
setLayout(new BorderLayout());
add(course = new List(10), "Center");
String[] courses = getCourseList();
if (courses!=null)
for (int i=0; i < courses.length; ++i) course.add(courses[i]);
Panel p = new Panel(new GridLayout());
Button b = new Button(cmd1); // Primary button
b.setActionCommand(cmd1);
b.addActionListener(listener);
p.add(b);
if (cmd2!=null) { // Secondary button?
b = new Button(cmd2);
b.setActionCommand(cmd2);
b.addActionListener(listener);
p.add(b);
}
add(p, "South"); // Add button-panel
}
// Return an array of strings representing a list of courses
private String[] getCourseList() {
courses = EnrolDatabase.getCourses(); // Access database
if (courses==null) return null; // Access failed?
String[] s = new String[courses.length];
CourseData c;
for (int i=0; i < courses.length; ++i) {
c = courses[i];
s[i] = c.code + " " + c.title;
if (c.numEnrol>=c.maxEnrol) s[i] += " (SOLD OUT)";
}
return s;
}
// Return the currently selected text (ie. course)
public String getText() { return course.getSelectedItem(); }
// Return the CourseData for the currently selected course
CourseData getCourse() {
int i = course.getSelectedIndex();
return (i < 0) ? null : courses[i];
}
}
// Enrolment details panel
class DetailsPanel extends Panel {
private CourseInfo course;
private StudentInfo student;
private PaymentInfo payment;
DetailsPanel(ActionListener listener, String submit, String cancel) {
setLayout(new BorderLayout());
Panel form = new Panel(new BorderLayout());
Panel p = new Panel(new BorderLayout());
p.add(course = new CourseInfo(), "North");
p.add(student = new StudentInfo(), "Center");
form.add(p, "North");
form.add(payment = new PaymentInfo() {
public void itemStateChanged(ItemEvent e) {
super.itemStateChanged(e);
Lib.parentFrame(this).validate();
}
}, "South");
add(form, "North");
p = new Panel(new GridLayout());
Button b = new Button(submit);
b.setActionCommand(submit);
b.addActionListener(listener);
p.add(b);
b = new Button(cancel);
b.setActionCommand(cancel);
b.addActionListener(listener);
p.add(b);
add(p, "South");
}
// Set the course description for this enrolment panel
void setCourse(String course) { this.course.setText(course); }
// Update the database
boolean submit(CourseData data) {
if (student.updateTable(data)) // Update student table
if (payment.updateTable(data)) // Update payment table
return true;
return false;
}
// Return a summary of the text on this enrolment panel
String getText() {
return "\n" + course.getText() +
"\nSTUDENT DETAILS\n" + student.getText() +
"\nPAYMENT DETAILS\n" + payment.getText();
}
}
// Application summary panel
class SummaryPanel extends Panel {
private static final String
msgOk = "\n Your application has been registered. Thank you.\n\n" +
" This is your completed course application form.\n\n" +
" To complete your application, print this page, sign\n" +
" the printed copy and fax it to the enrolment centre.",
msgErr= "\n A SYSTEM ERROR HAS OCCURRED!\n\n" +
" Please re-submit your application.",
msgSig= "\nSIGNED:";
private TextArea msg, info;
// Constructor
SummaryPanel(ActionListener listener, String finish) {
setLayout(new BorderLayout());
add(msg = new TextArea("", 7, 40, TextArea.SCROLLBARS_NONE), "North");
msg.setEditable(false);
add(info = new TextArea("", 17, 40, TextArea.SCROLLBARS_NONE), "Center");
info.setEditable(false);
Button b = new Button(finish);
b.setActionCommand(finish);
b.addActionListener(listener);
add(b, "South");
}
// Set the text for this SummaryPanel
void setText(String text) {
if (text!=null) { // All okay?
msg.setForeground(Color.blue);
msg.setText(msgOk);
info.setText(text + msgSig);
} else { // Error
msg.setForeground(Color.red);
msg.setText(msgErr);
info.setText("");
}
}
}
// --------------------------------------------------------------------------------
// The following components are extra display-panels for the ClerkPanel component
// --------------------------------------------------------------------------------
// Generic super-class for text-output panels
class TextPanel extends Panel {
TextArea text;
// Constructor
TextPanel(ActionListener listener, String cancel) {
super(new BorderLayout());
add(text = new TextArea(), "Center");
text.setEditable(false);
Button b = new Button(cancel);
b.setActionCommand(cancel);
b.addActionListener(listener);
add(b, "South");
}
// Return a list of student-information as a text string
String getStudentList(StudentData[] students) {
String text = "";
if (students!=null) for (int i=0; i < students.length; ++i) {
StudentData s = students[i];
text += s.name;
if (s.attended||s.paid) {
text += " (";
if (s.attended) text += "attended";
if (s.attended&&s.paid) text += ", ";
if (s.paid) text += "paid";
text += ")";
}
text += "\n";
}
return text;
}
}
// Student status information
class StatusPanel extends TextPanel {
// Constructor
StatusPanel(ActionListener listener, String cancel) { super(listener, cancel); }
// Create a list of the students on a given course
void setCourse(CourseData data) {
String s = data.code + " " + data.title + "\n\n";
s += getStudentList(EnrolDatabase.getStudents(data));
text.setText(s);
}
}
// Outstanding balance information
class UnpaidPanel extends TextPanel {
// Constructor
UnpaidPanel(ActionListener listener, String cancel) {
super(listener, cancel);
String s = getStudentList(EnrolDatabase.getUnpaid());
text.setText("Unpaid Balances\n\n" + s);
}
}
// ---------------------
// Main display panels
// ---------------------
// Generic super-class for top-level display panels
class MainPanel extends Panel {
Panel oldPanel = null;
// Constructor
MainPanel() { setLayout(new BorderLayout()); }
// Replace oldPanel with newPanel
void setPanel(Panel newPanel) {
remove(oldPanel);
add(newPanel, "Center");
Lib.parentFrame(this).validate();
oldPanel = newPanel;
}
}
// Enrolment display panel
class EnrolPanel extends MainPanel implements ActionListener {
private CoursePanel course;
private DetailsPanel details;
private SummaryPanel summary;
private CourseData data;
// Constructor
EnrolPanel() {
course = new CoursePanel(this, "Enrol");
details = new DetailsPanel(this, "Submit", "Cancel");
summary = new SummaryPanel(this, "Finish");
add(oldPanel = course, "Center");
}
// Process button-presses from the sub-panels
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("Enrol")) { // Course -> Details
data = course.getCourse(); // Get course data
if (data==null) return; // No course selected?
details.setCourse(course.getText()); // Set course name
setPanel(details); // Swap panels
} else if (cmd.equals("Cancel")) { // Details -> Course
setPanel(course); // Swap panels
} else if (cmd.equals("Submit")) { // Details -> Summary
String s = details.submit(data) ? details.getText() : null;
summary.setText(s); // Write summary
setPanel(summary); // Swap panels
} else if (cmd.equals("Finish")) { // Summary -> Course
setPanel(course); // Swap panels
}
}
}
// Clerk display panel
class ClerkPanel extends MainPanel implements ActionListener {
private CoursePanel course;
private StatusPanel status;
private UnpaidPanel unpaid;
private CourseData data;
ClerkPanel() {
course = new CoursePanel(this, "Students", "Unpaid");
status = new StatusPanel(this, "Cancel");
unpaid = new UnpaidPanel(this, "Cancel");
add(oldPanel = course, "Center");
}
// Process button-presses from the sub-panels
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("Students")) { // Course -> Status
data = course.getCourse(); // Get course data
if (data==null) return; // No course selected?
status.setCourse(data); // Create status list
setPanel(status); // Swap panels
} else if (cmd.equals("Unpaid")) { // Course -> Unpaid
setPanel(unpaid); // Swap panels
} else if (cmd.equals("Cancel")) { // Status/Unpaid -> Course
setPanel(course); // Swap panels
}
}
}
// Main program class
public class Enrol extends java.applet.Applet {
// Debug-mode flags
static final boolean
READTEST = true, // Ignore read-errors
WRITETEST = true; // Ignore write-errors
// Start the applet
public void init() {
setLayout(new BorderLayout());
String s = getParameter("mode"); // Get mode from HTML
Component c;
if (s!=null) if (!s.equals("clerk")) s = null; // Clerk-mode parameter?
if (s!=null) c = new ClerkPanel(); // Clerk mode
else c = new EnrolPanel(); // Enrolment mode
add(c, "Center"); // Add chosen panel
}
// Start the application
public static void main(String[] args) {
Frame win = new Frame("Course Enrolment System");
win.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e){ e.getWindow().dispose();}
public void windowClosed(WindowEvent e) { System.exit(0); }
});
win.add(new ClerkPanel(), "Center"); // Clerk panel
win.pack();
win.show();
}
}
Go To:
IM264: Object-Oriented Concepts