diff options
Diffstat (limited to 'src/uk/org/ury/library')
-rw-r--r-- | src/uk/org/ury/library/LibraryRequestHandler.java | 144 | ||||
-rw-r--r-- | src/uk/org/ury/library/LibraryUtils.java | 112 | ||||
-rw-r--r-- | src/uk/org/ury/library/item/LibraryItem.java | 26 | ||||
-rw-r--r-- | src/uk/org/ury/library/item/LibraryItemProperty.java | 62 | ||||
-rw-r--r-- | src/uk/org/ury/library/viewer/LibraryTableModel.java | 184 | ||||
-rw-r--r-- | src/uk/org/ury/library/viewer/LibraryViewer.java | 95 | ||||
-rw-r--r-- | src/uk/org/ury/library/viewer/LibraryViewerPanel.java | 10 | ||||
-rw-r--r-- | src/uk/org/ury/library/viewer/library_viewer_gui.xml | 12 |
8 files changed, 554 insertions, 91 deletions
diff --git a/src/uk/org/ury/library/LibraryRequestHandler.java b/src/uk/org/ury/library/LibraryRequestHandler.java new file mode 100644 index 0000000..30bb7fc --- /dev/null +++ b/src/uk/org/ury/library/LibraryRequestHandler.java @@ -0,0 +1,144 @@ +/** + * + */ +package uk.org.ury.library; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import uk.org.ury.database.DatabaseDriver; +import uk.org.ury.database.UserClass; +import uk.org.ury.database.exceptions.ConnectionFailureException; +import uk.org.ury.database.exceptions.MissingCredentialsException; +import uk.org.ury.database.exceptions.QueryFailureException; +import uk.org.ury.library.exceptions.EmptySearchException; +import uk.org.ury.library.item.LibraryItem; +import uk.org.ury.server.Server; +import uk.org.ury.server.RequestHandler; +import uk.org.ury.server.exceptions.HandleFailureException; +import uk.org.ury.server.protocol.Directive; + +/** + * A request handler for library queries. + * + * @author Matt Windsor + */ + +public class LibraryRequestHandler implements RequestHandler +{ + /** + * Handle a server GET request (that is, a request for data + * output). + * + * @param parameters A key-value map of parameters supplied with + * the server request. Typically, the function + * parameter will detail the function that the + * request handler is expected to perform. + * + * @param server The server from which the request originated. + * This will be able to provide the handler with + * pooled resources, for example the database. + * + * @return A list of lines to return in the body of the + * server's response to the client. + * + * @throws HandleFailureException if the handler cannot + * handle the request. + */ + + @Override + public List<String> + handleGetRequest (Map<String, String> parameters, Server server) + throws HandleFailureException + { + List<String> response = new ArrayList<String> (); + + if (parameters.containsKey ("function")) + { + String function = parameters.get ("function"); + + if (function.equals ("search")) + { + doSearch (parameters, response, server); + } + else if (function.equals ("help")) + { + response.add ("INFO: Available functions:"); + response.add ("INFO: search (string) - search library for string."); + } + else + throw new HandleFailureException ("Unknown function: " + + function + ". (Try 'function=help'.)"); + + } + else + throw new HandleFailureException ("No function provided. (Try 'function=help'.)"); + + return response; + } + + + /** + * Perform a library search, populating the response list. + * + * @param parameters A key-value map of parameters supplied with + * the server request. Typically, the function + * parameter will detail the function that the + * request handler is expected to perform. + * + * @param response The response list to populate. + * + * @param server The server providing database resources. + * + * @throws HandleFailureException if an error occurs + * that thwarts the handling of the request. + */ + + private void + doSearch (Map<String, String> parameters, + List<String> response, Server server) + throws HandleFailureException + { + if (parameters.containsKey ("search") == false) + throw new HandleFailureException ("Search term is missing."); + else if (parameters.get ("search") == null) + throw new HandleFailureException ("Search term is null."); + + String search = parameters.get ("search"); + DatabaseDriver dd = null; + + try + { + dd = server.getDatabaseConnection (UserClass.READ_ONLY); + } + catch (MissingCredentialsException e) + { + throw new HandleFailureException (e.getMessage ()); + } + catch (ConnectionFailureException e) + { + throw new HandleFailureException (e.getMessage ()); + } + + try + { + for (LibraryItem li : LibraryUtils.search (dd, search)) + { + response.add (Directive.ITEM_START.toString ()); + + response.addAll (li.asResponse ()); + + response.add (Directive.ITEM_END.toString ()); + } + } + catch (QueryFailureException e) + { + throw new HandleFailureException (e.getMessage ()); + } + catch (EmptySearchException e) + { + throw new HandleFailureException (e.getMessage ()); + } + } +} diff --git a/src/uk/org/ury/library/LibraryUtils.java b/src/uk/org/ury/library/LibraryUtils.java index a280738..8a71285 100644 --- a/src/uk/org/ury/library/LibraryUtils.java +++ b/src/uk/org/ury/library/LibraryUtils.java @@ -8,12 +8,16 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map; import uk.org.ury.database.DatabaseDriver; import uk.org.ury.database.exceptions.QueryFailureException; -import uk.org.ury.library.LibraryItemProperty; import uk.org.ury.library.exceptions.EmptySearchException; +import uk.org.ury.library.item.LibraryItem; +import uk.org.ury.library.item.LibraryItemProperty; +import uk.org.ury.server.protocol.Directive; /** @@ -77,13 +81,9 @@ public class LibraryUtils + " t.artist, recordlabel AS label, status, media AS medium, format," + " datereleased, EXTRACT(EPOCH FROM dateadded) as dateadded," + " EXTRACT(EPOCH FROM datetime_lastedit) AS dateedited," - + " shelfletter, shelfnumber, cdid, memberid_add, memberid_lastedit," - + " digitised, clean," - + " a.fname AS fnameadd, a.sname AS snameadd, b.fname AS fnameedit, b.sname AS snameedit" + + " shelfletter, shelfnumber, cdid, digitised, clean" + " FROM rec_record AS r" + " INNER JOIN rec_track AS t ON (r.recordid = t.recordid)" - + " INNER JOIN member AS a ON (a.memberid = r.memberid_add)" - + " LEFT JOIN member AS b ON (b.memberid = r.memberid_lastedit)" + " WHERE t.title ILIKE ?" + " OR t.artist ILIKE ?" + " OR r.title ILIKE ?" @@ -126,7 +126,7 @@ public class LibraryUtils { // Translate SQL columns into a list of properties. - HashMap<LibraryItemProperty, String> properties = new HashMap<LibraryItemProperty, String> (); + Map<LibraryItemProperty, String> properties = new HashMap<LibraryItemProperty, String> (); for (LibraryItemProperty p : LibraryItemProperty.values ()) { @@ -144,4 +144,102 @@ public class LibraryUtils return new LibraryItem (properties); } + + + /** + * Construct items from a server response body. + * + * @param response The list of strings that constitute the response. + * + * @return a list of LibraryItems corresponding to the item + * stanzas in the response. + */ + + public static List<LibraryItem> + extractItemsFromResponse (List<String> response) + { + List<LibraryItem> result = new ArrayList<LibraryItem> (); + + boolean inItem = false; + List<String> itemBuffer = new ArrayList<String> (); + + for (String line : response) + { + if (inItem == false) + { + if (line.startsWith (Directive.ITEM_START.toString ())) + { + inItem = true; + itemBuffer.clear (); + } + } + + if (inItem == true) + { + itemBuffer.add (line); + + if (line.startsWith (Directive.ITEM_END.toString ())) + { + inItem = false; + result.add (createItemFromResponse (itemBuffer)); + } + } + } + + return result; + } + + + /** + * Construct a new item from a server response fragment. + * + * @param response The list of strings that constitutes the response. + * The list must begin with Directive.ITEM_START and + * end with Directive.ITEM_END's string + * representations and otherwise contain solely + * Directive.ITEM_PROPERTY lines. + * + * @return a LibraryItem embodying the properties described + * in the response fragment. + * + * @throws IllegalArgumentException if the response is + * malformed or null, or if the instantiation of + * DatabaseItem does not use String as its data type. + */ + + public static LibraryItem + createItemFromResponse (List<String> response) + { + // TODO: More appropriate exceptions. + + if (response == null) + throw new IllegalArgumentException ("Response is null."); + else if (response.get (0).equals (Directive.ITEM_START.toString ()) + && response.get (response.size () - 1) + .equals (Directive.ITEM_END.toString ())) + { + // Response of size 2 must be blank. + if (response.size () <= 2) + throw new IllegalArgumentException ("Blank response."); + + Map<LibraryItemProperty, String> properties = new HashMap<LibraryItemProperty, String> (); + + for (int i = 0; i < response.size () - 1; i++) + { + // TODO: fan out impl. details to separate class + if (response.get (i) + .startsWith (Directive.ITEM_PROPERTY.toString ())) + { + String[] responseTuple = response.get (i).split (":="); + + properties.put (LibraryItemProperty.getFromSQL (responseTuple[1]), + responseTuple[2]); + } + } + + return new LibraryItem (properties); + } + else + throw new IllegalArgumentException ("Malformed response."); + } } diff --git a/src/uk/org/ury/library/item/LibraryItem.java b/src/uk/org/ury/library/item/LibraryItem.java new file mode 100644 index 0000000..d2a790b --- /dev/null +++ b/src/uk/org/ury/library/item/LibraryItem.java @@ -0,0 +1,26 @@ +/** + * + */ +package uk.org.ury.library.item; + + +import java.util.Map; + +import uk.org.ury.database.DatabaseItem; + + +/** + * An item in the URY library. + * + * @author Matt Windsor + */ + +public class LibraryItem extends DatabaseItem<LibraryItemProperty, + String> +{ + public + LibraryItem (Map<LibraryItemProperty, String> properties) + { + super (properties); + } +}
\ No newline at end of file diff --git a/src/uk/org/ury/library/item/LibraryItemProperty.java b/src/uk/org/ury/library/item/LibraryItemProperty.java new file mode 100644 index 0000000..44f5f22 --- /dev/null +++ b/src/uk/org/ury/library/item/LibraryItemProperty.java @@ -0,0 +1,62 @@ +package uk.org.ury.library.item; + +/** + * The parameters that are stored in the LibraryItem. + * + * @author Matt Windsor + */ + +public enum LibraryItemProperty + { + // Constant SQL identifier + TITLE ("title"), + ALBUM ("album"), + ARTIST ("artist"), + LABEL ("label"), + STATUS ("status"), + MEDIUM ("medium"), + FORMAT ("format"), + DATE_RELEASED ("datereleased"), + DATE_ADDED ("dateadded"), + DATE_EDITED ("dateedited"), + SHELF_LETTER ("shelfletter"), + SHELF_NUMBER ("shelfnumber"), + CD_ID ("cdid"), + IS_DIGITISED ("digitised"), + IS_CLEAN ("clean"); + + + public final String sql; + + + private + LibraryItemProperty (String sql) + { + this.sql = sql; + } + + + /** + * Retrieve a LibraryItemProperty given its SQL identifier. + * + * @param string The SQL identifier. + * @return The first property to match. + * + * @throws IllegalArgumentException if no matches were + * found. + */ + + public static LibraryItemProperty + getFromSQL (String string) + { + // TODO: Better exception? + + for (LibraryItemProperty prop : values ()) + { + if (prop.sql.equals (string)) + return prop; + } + + throw new IllegalArgumentException ("Nonexistent property SQL."); + } + };
\ No newline at end of file diff --git a/src/uk/org/ury/library/viewer/LibraryTableModel.java b/src/uk/org/ury/library/viewer/LibraryTableModel.java new file mode 100644 index 0000000..1ce5709 --- /dev/null +++ b/src/uk/org/ury/library/viewer/LibraryTableModel.java @@ -0,0 +1,184 @@ +/** + * + */ +package uk.org.ury.library.viewer; + +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +import uk.org.ury.database.exceptions.MissingPropertyException; +import uk.org.ury.library.item.LibraryItem; +import uk.org.ury.library.item.LibraryItemProperty; + + +/** + * A table model for the library viewer. + * + * @author Matt Windsor + */ + +public class LibraryTableModel extends AbstractTableModel +{ + + /** + * + */ + + private static final long serialVersionUID = -1744980619128903509L; + + private List<LibraryItem> data; + + + /** + * Construct a new table model. + * + * @param data The list of data on which the model will be based. + */ + + public + LibraryTableModel (List<LibraryItem> data) + { + this.data = data; + } + + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getColumnCount() + */ + + @Override + public int + getColumnCount () + { + return 6; + } + + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getRowCount() + */ + + @Override + public int + getRowCount () + { + return data.size (); + } + + + /** + * @param c The column whose class should be investigated. + * + * @return the column class of column c. + */ + + @Override + public Class<?> + getColumnClass (int c) + { + return getValueAt (0, c).getClass (); + } + + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getValueAt(int, int) + */ + + @Override + public Object + getValueAt (int rowIndex, int columnIndex) + { + LibraryItem li = data.get (rowIndex); + + try + { + String[] columnData = {li.get (LibraryItemProperty.TITLE), + li.get (LibraryItemProperty.ARTIST), + li.get (LibraryItemProperty.ALBUM)}; + + switch (columnIndex) + { + default: // Title, artist, album, unknown + + if (columnIndex >= columnData.length) + return "Unknown"; + else + return columnData[columnIndex]; + + case 3: // Medium + + // TODO: Make this less kludge-y + + String mediumString = li.get (LibraryItemProperty.MEDIUM); + + if (mediumString.equals ("c")) + return "Compact Disc"; + else if (mediumString.equals ("7")) + return "7\" Vinyl"; + else if (mediumString.equals ("2")) + return "12\" Vinyl"; + else + return "Unrecognised"; + + case 4: // Clean? + + // Return true if marked true, false if marked false or unknown etc. + + String cleanString = li.get (LibraryItemProperty.IS_CLEAN); + + // TODO: Nicer way of showing this + + if (cleanString.equals ("y")) + return "Yes"; + else if (cleanString.equals ("n")) + return "No"; + else + return "???"; + + case 5: // isDigitised + + // Return true if marked true, false if marked false or unknown etc. + + String digitisedString = li.get (LibraryItemProperty.IS_DIGITISED); + + if (digitisedString.equals ("t")) + return true; + else + return false; + } + } + catch (MissingPropertyException e) + { + return "Unknown"; + } + } + + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getColumnName(int, int) + */ + + @Override + public String + getColumnName (int index) + { + switch (index) + { + case 0: + return "Title"; + case 1: + return "Artist"; + case 2: + return "Album"; + case 3: + return "Medium"; + case 4: + return "Clean?"; + case 5: + return "On system?"; + default: + return "ERROR"; + } + } +} diff --git a/src/uk/org/ury/library/viewer/LibraryViewer.java b/src/uk/org/ury/library/viewer/LibraryViewer.java index a98fa54..9587d5e 100644 --- a/src/uk/org/ury/library/viewer/LibraryViewer.java +++ b/src/uk/org/ury/library/viewer/LibraryViewer.java @@ -1,27 +1,21 @@ package uk.org.ury.library.viewer; -import java.lang.reflect.InvocationTargetException; - import java.util.ArrayList; import java.util.List; -import javax.swing.SwingUtilities; - import uk.org.ury.config.ConfigReader; import uk.org.ury.database.DatabaseDriver; import uk.org.ury.database.UserClass; import uk.org.ury.database.exceptions.MissingCredentialsException; import uk.org.ury.database.exceptions.QueryFailureException; import uk.org.ury.frontend.AbstractFrontendModule; -import uk.org.ury.frontend.FrontendError; -import uk.org.ury.frontend.FrontendFrame; import uk.org.ury.frontend.FrontendMaster; import uk.org.ury.frontend.FrontendModulePanel; - -import uk.org.ury.library.LibraryItem; +import uk.org.ury.frontend.exceptions.UICreationFailureException; import uk.org.ury.library.LibraryUtils; import uk.org.ury.library.exceptions.EmptySearchException; +import uk.org.ury.library.item.LibraryItem; public class LibraryViewer extends AbstractFrontendModule { @@ -33,7 +27,6 @@ public class LibraryViewer extends AbstractFrontendModule private DatabaseDriver dd; private List<LibraryItem> libraryList; private LibraryViewerPanel panel; - private FrontendFrame frame; private ConfigReader config; @@ -53,64 +46,9 @@ public class LibraryViewer extends AbstractFrontendModule System.out.println(e); } - frame = null; libraryList = new ArrayList<LibraryItem> (); panel = null; } - - - /** - * Initialise the library viewer frontend as an applet. - */ - - public void - init () - { - frame = null; - libraryList = new ArrayList<LibraryItem> (); - panel = null; - - - try - { - SwingUtilities.invokeAndWait (new Runnable () - { - public void - run () - { - panel.setOpaque (true); - setContentPane (panel); - - runFrontend (null); - } - - }); - } - catch (InterruptedException e) - { - // TODO Auto-generated catch block - e.printStackTrace (); - } - catch (InvocationTargetException e) - { - // TODO Auto-generated catch block - e.printStackTrace (); - } - } - - - /** - * Run the library viewer frontend as an applet. - */ - - public void - start () - { - frame = null; - panel = new LibraryViewerPanel (this, null); - - add (panel); - } /** @@ -129,14 +67,22 @@ public class LibraryViewer extends AbstractFrontendModule catch (MissingCredentialsException e) { // TODO: Privilege de-escalation - FrontendError.reportFatal (e.getMessage (), frame); + master.fatalError (e.getMessage ()); } catch (Exception f) { - FrontendError.reportFatal (f.getMessage (), frame); + master.fatalError (f.getMessage ()); } - panel = new LibraryViewerPanel (this, master); + try + { + panel = new LibraryViewerPanel (this, master); + } + catch (UICreationFailureException e) + { + master.fatalError (e.getMessage ()); + } + return panel; } @@ -152,19 +98,16 @@ public class LibraryViewer extends AbstractFrontendModule * * @throws EmptySearchException if the search string is * empty or null (from LibraryUtils.search). + * + * @throws QueryFailureException if the search query + * fails (from LibraryUtils.search). */ public void - doSearch (String search) throws EmptySearchException + doSearch (String search) + throws EmptySearchException, QueryFailureException { - try - { - libraryList = LibraryUtils.search (dd, search); - } - catch (QueryFailureException e) - { - FrontendError.reportFatal (e.getMessage (), frame); - } + libraryList = LibraryUtils.search (dd, search); } diff --git a/src/uk/org/ury/library/viewer/LibraryViewerPanel.java b/src/uk/org/ury/library/viewer/LibraryViewerPanel.java index 7638822..e453095 100644 --- a/src/uk/org/ury/library/viewer/LibraryViewerPanel.java +++ b/src/uk/org/ury/library/viewer/LibraryViewerPanel.java @@ -14,9 +14,10 @@ import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.SwingWorker; +import uk.org.ury.database.exceptions.QueryFailureException; import uk.org.ury.frontend.FrontendMaster; import uk.org.ury.frontend.FrontendModulePanel; -import uk.org.ury.library.LibraryTableModel; +import uk.org.ury.frontend.exceptions.UICreationFailureException; import uk.org.ury.library.exceptions.EmptySearchException; @@ -59,10 +60,13 @@ public class LibraryViewerPanel extends FrontendModulePanel * @param viewer The LibraryViewer controlling this LibraryViewerPanel. * * @param master The FrontendMaster driving the frontend. + * + * @throws UICreationFailureException if the UI creation fails. */ public LibraryViewerPanel (LibraryViewer viewer, FrontendMaster master) + throws UICreationFailureException { super (viewer, "library_viewer_gui.xml", master); @@ -138,6 +142,10 @@ public class LibraryViewerPanel extends FrontendModulePanel { master.doSearch (searchField.getText ()); } + catch (QueryFailureException e) + { + searchFailureMessage = "Search failed: " + e.getMessage (); + } catch (EmptySearchException e) { searchFailureMessage = "Please type in a search term."; diff --git a/src/uk/org/ury/library/viewer/library_viewer_gui.xml b/src/uk/org/ury/library/viewer/library_viewer_gui.xml index 6b7e745..5177e3f 100644 --- a/src/uk/org/ury/library/viewer/library_viewer_gui.xml +++ b/src/uk/org/ury/library/viewer/library_viewer_gui.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <panel layout="BorderLayout"> - <hbox constraints="BorderLayout.NORTH" border="EmptyBorder(5,5,5,5)" size="640,32"> - <label text="Search for:" labelfor="searchField" displayedmnemonic="VK_F" - font="Verdana-BOLD-14" /> + <hbox constraints="BorderLayout.NORTH" border="EmptyBorder(5,5,5,5)"> + <label text="Search for:" labelfor="searchField" displayedmnemonic="VK_F" /> <hbox border="EmptyBorder(0,5,0,5)"> <textfield id="searchField" mnemonic="VK_F" action="search" /> </hbox> - <button id="searchButton" text="Search" mnemonic="VK_S" action="search" - font="Verdana-BOLD-14" /> + <button id="searchButton" text="Search" mnemonic="VK_S" action="search" /> </hbox> <hbox constraints="BorderLayout.CENTER" border="EmptyBorder(0,5,0,5)"> @@ -16,14 +14,14 @@ </scrollpane> <panel id="searchingPanel" constraints="BorderLayout.CENTER" visible="false" layout="BorderLayout"> - <label id="searchingLabel" text="Searching..." font="Verdana-BOLD-16" + <label id="searchingLabel" text="Searching..." constraints="BorderLayout.CENTER" horizontalalignment="CENTER" /> <progressbar id="searchingProgressBar" indeterminate="true" constraints="BorderLayout.SOUTH" /> </panel> <panel id="messagePanel" constraints="BorderLayout.CENTER" visible="false" layout="BorderLayout"> - <label id="messageLabel" text="Searching..." font="Verdana-BOLD-16" + <label id="messageLabel" text="You shouldn't see this." constraints="BorderLayout.CENTER" horizontalalignment="CENTER" /> </panel> </hbox> |