aboutsummaryrefslogtreecommitdiff
path: root/src/uk/org/ury/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/uk/org/ury/common')
-rw-r--r--src/uk/org/ury/common/library/LibraryUtils.java144
-rw-r--r--src/uk/org/ury/common/library/exceptions/EmptySearchException.java49
-rw-r--r--src/uk/org/ury/common/library/item/LibraryItem.java26
-rw-r--r--src/uk/org/ury/common/library/item/LibraryItemProperty.java62
-rw-r--r--src/uk/org/ury/common/protocol/Directive.java18
-rw-r--r--src/uk/org/ury/common/protocol/ProtocolUtils.java85
-rw-r--r--src/uk/org/ury/common/protocol/Status.java17
-rw-r--r--src/uk/org/ury/common/protocol/exceptions/DecodeFailureException.java32
-rw-r--r--src/uk/org/ury/common/protocol/exceptions/InvalidMessageException.java33
-rw-r--r--src/uk/org/ury/common/show/ShowChannel.java120
-rw-r--r--src/uk/org/ury/common/show/ShowUtils.java190
-rw-r--r--src/uk/org/ury/common/show/ShowUtils.properties4
-rw-r--r--src/uk/org/ury/common/show/item/ShowItem.java68
-rw-r--r--src/uk/org/ury/common/show/item/ShowItemProperty.java26
-rw-r--r--src/uk/org/ury/common/show/item/package.html14
15 files changed, 888 insertions, 0 deletions
diff --git a/src/uk/org/ury/common/library/LibraryUtils.java b/src/uk/org/ury/common/library/LibraryUtils.java
new file mode 100644
index 0000000..335b8b5
--- /dev/null
+++ b/src/uk/org/ury/common/library/LibraryUtils.java
@@ -0,0 +1,144 @@
+/**
+ *
+ */
+package uk.org.ury.common.library;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import uk.org.ury.backend.database.DatabaseDriver;
+import uk.org.ury.backend.database.exceptions.QueryFailureException;
+import uk.org.ury.common.library.exceptions.EmptySearchException;
+import uk.org.ury.common.library.item.LibraryItem;
+import uk.org.ury.common.library.item.LibraryItemProperty;
+
+
+
+/**
+ * A set of common utility routines to facilitate the extraction of
+ * library items from the library areas of the URY database.
+ *
+ * @author Matt Windsor
+ */
+
+public class LibraryUtils
+{
+ /**
+ * Perform a library search.
+ *
+ * @param db The database to query.
+ *
+ * @param search The search fragment to include in the search.
+ * Can be empty or null.
+ *
+ * @throws IllegalArgumentException if the search term is
+ * are null.
+ *
+ * @throws QueryFailureException if the database backend
+ * yielded an error while executing the search
+ * query.
+ *
+ * @throws EmptySearchException if the search term is
+ * empty (to be handled as a user error).
+ *
+ * @return a list of LibraryItems matching the search terms.
+ */
+
+ public static List<LibraryItem>
+ search (DatabaseDriver db, String search)
+ throws QueryFailureException, EmptySearchException
+ {
+ if (db == null)
+ throw new IllegalArgumentException ("Database handle is null.");
+
+ if (search == null)
+ throw new IllegalArgumentException ("Search string is null.");
+
+ List<LibraryItem> results = new ArrayList<LibraryItem> ();
+
+
+ // Return empty set if the search term is null.
+
+ if (search.equals(""))
+ throw new EmptySearchException ();
+
+
+ ResultSet rs = null;
+
+ Object[] params = {"%" + search + "%", "%" + search + "%", "%" + search + "%"};
+
+ try
+ {
+ rs = db.executeQuery (
+ "SELECT r.title AS album, t.title,"
+ + " 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, digitised, clean"
+ + " FROM rec_record AS r"
+ + " INNER JOIN rec_track AS t ON (r.recordid = t.recordid)"
+ + " WHERE t.title ILIKE ?"
+ + " OR t.artist ILIKE ?"
+ + " OR r.title ILIKE ?"
+ + " ORDER BY digitised DESC, medium ASC, r.title ASC,"
+ + " t.artist ASC, t.title ASC;", params, 50);
+ }
+ catch (SQLException e)
+ {
+ throw new QueryFailureException (e.getMessage ());
+ }
+
+ try
+ {
+ while (rs.next ())
+ {
+ results.add (translateRow (rs));
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new QueryFailureException (e.getMessage ());
+ }
+
+ return results;
+ }
+
+
+ /**
+ * Translate a row retrieved from the database into a LibraryItem.
+ *
+ * @param rs The result-set, or database cursor, pointing to the
+ * row to translate.
+ *
+ * @return A new LibraryItem containing the properties extracted
+ * from the translated row.
+ */
+
+ private static LibraryItem
+ translateRow (ResultSet rs)
+ {
+ // Translate SQL columns into a list of properties.
+
+ Map<LibraryItemProperty, String> properties = new HashMap<LibraryItemProperty, String> ();
+
+ for (LibraryItemProperty p : LibraryItemProperty.values ())
+ {
+ try
+ {
+ properties.put (p, rs.getString (p.sql));
+ }
+ catch (SQLException e)
+ {
+ // Ignore this, as it is almost certainly just a non-existent
+ // property.
+ }
+ }
+
+
+ return new LibraryItem (properties);
+ }
+}
diff --git a/src/uk/org/ury/common/library/exceptions/EmptySearchException.java b/src/uk/org/ury/common/library/exceptions/EmptySearchException.java
new file mode 100644
index 0000000..fc4ed68
--- /dev/null
+++ b/src/uk/org/ury/common/library/exceptions/EmptySearchException.java
@@ -0,0 +1,49 @@
+/**
+ *
+ */
+package uk.org.ury.common.library.exceptions;
+
+/**
+ * Exception thrown when a library search is initiated
+ * in which the query string is null.
+ *
+ * Frontends should handle this nicely. Do NOT treat this
+ * as a fatal error!
+ *
+ * @author Matt Windsor
+ */
+
+public class EmptySearchException extends Exception
+{
+
+ /**
+ * Change this! ---v
+ */
+
+ private static final long serialVersionUID = -397479334359858162L;
+
+
+ /**
+ * Construct a new EmptySearchException with a
+ * default reason.
+ */
+
+ public
+ EmptySearchException ()
+ {
+ super ("Query string was empty.");
+ }
+
+
+ /**
+ * Construct a new EmptySearchException.
+ *
+ * @param reason The explanation for the exception.
+ */
+
+ public
+ EmptySearchException (String reason)
+ {
+ super (reason);
+ }
+}
diff --git a/src/uk/org/ury/common/library/item/LibraryItem.java b/src/uk/org/ury/common/library/item/LibraryItem.java
new file mode 100644
index 0000000..3e81041
--- /dev/null
+++ b/src/uk/org/ury/common/library/item/LibraryItem.java
@@ -0,0 +1,26 @@
+/**
+ *
+ */
+package uk.org.ury.common.library.item;
+
+
+import java.util.Map;
+
+import uk.org.ury.backend.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/common/library/item/LibraryItemProperty.java b/src/uk/org/ury/common/library/item/LibraryItemProperty.java
new file mode 100644
index 0000000..2e4d2ef
--- /dev/null
+++ b/src/uk/org/ury/common/library/item/LibraryItemProperty.java
@@ -0,0 +1,62 @@
+package uk.org.ury.common.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/common/protocol/Directive.java b/src/uk/org/ury/common/protocol/Directive.java
new file mode 100644
index 0000000..e1edd98
--- /dev/null
+++ b/src/uk/org/ury/common/protocol/Directive.java
@@ -0,0 +1,18 @@
+/**
+ *
+ */
+package uk.org.ury.common.protocol;
+
+/**
+ * Directives supported by the protocol.
+ *
+ * @author Matt Windsor
+ */
+
+public enum Directive
+ {
+ INFO, // Information string (can usually be ignored)
+ ITEMS, // Item
+ STATUS, // Status code (from the enum Status)
+ REASON; // Error reason
+ }
diff --git a/src/uk/org/ury/common/protocol/ProtocolUtils.java b/src/uk/org/ury/common/protocol/ProtocolUtils.java
new file mode 100644
index 0000000..8686eb7
--- /dev/null
+++ b/src/uk/org/ury/common/protocol/ProtocolUtils.java
@@ -0,0 +1,85 @@
+/**
+ *
+ */
+package uk.org.ury.common.protocol;
+
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import uk.org.ury.common.protocol.exceptions.DecodeFailureException;
+import uk.org.ury.common.protocol.exceptions.InvalidMessageException;
+
+/**
+ * Utilities for converting between strings encoded in the response protocol and
+ * collections of items, as well as validating and unpicking protocol messages.
+ *
+ * @author Matt Windsor
+ *
+ */
+
+public class ProtocolUtils {
+ /**
+ * Encode a key-value map into a protocol string.
+ *
+ * The map can contain strings, lists and other maps. Other types may be
+ * accepted by the underlying encoding engine, but the above types are the
+ * only ones explicitly accepted.
+ *
+ * @param items
+ * The key-value map of items, which should contain strings,
+ * lists and maps. The keys of any map should be protocol
+ * directives.
+ *
+ * @return A string containing the encoded representation of the map.
+ */
+ public static String encode(Map<String, Object> items) {
+ return JSONValue.toJSONString(items);
+ }
+
+ /**
+ * Decode a protocol string into a key-value map.
+ *
+ * @param string
+ * The string to decode.
+ *
+ * @return A key-value map mapping directives to strings, lists and maps.
+ *
+ * @throws DecodeFailureException
+ * if the decoding engine returns something other than a map.
+ */
+ public static Map<?, ?> decode(String string) throws DecodeFailureException {
+ Object result = JSONValue.parse(string);
+
+ if (result instanceof JSONObject)
+ return (JSONObject) result;
+ else
+ throw new DecodeFailureException("Result not a map.");
+ }
+
+ /**
+ * Check if a response is flagged as having OK status.
+ *
+ * @param response
+ * The response message, as a key-value map (eg in decoded
+ * format).
+ *
+ * @return true if the response is flagged with OK status, false if not (eg
+ * ERROR status).
+ *
+ * @throws InvalidMessageException
+ * if the response is invalid (eg the status is missing).
+ */
+ public static boolean responseIsOK(Map<?, ?> response)
+ throws InvalidMessageException {
+ if (response.containsKey(Directive.STATUS.toString()) == false)
+ throw new InvalidMessageException("No status line in response.");
+
+ if ((response.get(Directive.STATUS.toString()) instanceof String) == false)
+ throw new InvalidMessageException("Status is not a string.");
+
+ return (((String) response.get(Directive.STATUS.toString()))
+ .equals(Status.OK.toString()));
+ }
+}
diff --git a/src/uk/org/ury/common/protocol/Status.java b/src/uk/org/ury/common/protocol/Status.java
new file mode 100644
index 0000000..b424fc9
--- /dev/null
+++ b/src/uk/org/ury/common/protocol/Status.java
@@ -0,0 +1,17 @@
+/**
+ *
+ */
+package uk.org.ury.common.protocol;
+
+
+/**
+ * Statuses that can follow the STATUS directory.
+ *
+ * @author Matt Windsor
+ */
+
+public enum Status
+ {
+ OK, // The request was processed OK; response should be valid
+ ERROR // An error occurred; message provided as REASON directive
+ }
diff --git a/src/uk/org/ury/common/protocol/exceptions/DecodeFailureException.java b/src/uk/org/ury/common/protocol/exceptions/DecodeFailureException.java
new file mode 100644
index 0000000..978ca7b
--- /dev/null
+++ b/src/uk/org/ury/common/protocol/exceptions/DecodeFailureException.java
@@ -0,0 +1,32 @@
+/**
+ *
+ */
+package uk.org.ury.common.protocol.exceptions;
+
+
+/**
+ * Exception thrown when the protocol decoder fails.
+ *
+ * @author Matt Windsor
+ */
+
+public class DecodeFailureException extends Exception
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3972492943653273528L;
+
+
+ /**
+ * Construct a new DecodeFailureException with a reason.
+ *
+ * @param reason The reason for throwing the exception.
+ */
+
+ public
+ DecodeFailureException (String reason)
+ {
+ super (reason);
+ }
+}
diff --git a/src/uk/org/ury/common/protocol/exceptions/InvalidMessageException.java b/src/uk/org/ury/common/protocol/exceptions/InvalidMessageException.java
new file mode 100644
index 0000000..739b501
--- /dev/null
+++ b/src/uk/org/ury/common/protocol/exceptions/InvalidMessageException.java
@@ -0,0 +1,33 @@
+/**
+ *
+ */
+package uk.org.ury.common.protocol.exceptions;
+
+
+/**
+ * Generic exception thrown when a protocol function cannot process a
+ * message due to an issue with the message.
+ *
+ * @author Matt Windsor
+ */
+
+public class InvalidMessageException extends Exception
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3972492943653273528L;
+
+
+ /**
+ * Construct a new InvalidMessageException with a reason.
+ *
+ * @param reason The reason for throwing the exception.
+ */
+
+ public
+ InvalidMessageException (String reason)
+ {
+ super (reason);
+ }
+}
diff --git a/src/uk/org/ury/common/show/ShowChannel.java b/src/uk/org/ury/common/show/ShowChannel.java
new file mode 100644
index 0000000..1c1b8ce
--- /dev/null
+++ b/src/uk/org/ury/common/show/ShowChannel.java
@@ -0,0 +1,120 @@
+/*
+ * ShowChannel.java
+ * ------------------
+ *
+ * Part of the URY Presentation Suite
+ *
+ * V0.00 2011/03/21
+ *
+ * (C) 2011 URY Computing
+ */
+
+package uk.org.ury.common.show;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractListModel;
+
+import uk.org.ury.common.show.item.ShowItem;
+
+/**
+ * A channel of ShowItems in a show.
+ *
+ * @author Matt Windsor
+ */
+public class ShowChannel extends AbstractListModel {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -4651104185166068150L;
+
+ /* Items enqueued in channel. */
+ private List<ShowItem> items;
+
+ /**
+ * Constructs a new, empty channel.
+ */
+ public ShowChannel() {
+ items = new ArrayList<ShowItem>();
+ }
+
+ /**
+ * Adds a new item to the channel.
+ *
+ * @param index
+ * The position at which to add the item.
+ *
+ * @param item
+ * The new item to add.
+ *
+ * @throws IllegalArgumentException
+ * if the item is null, the index is negative or the index is
+ * out of bounds.
+ */
+ public void add(int index, ShowItem item) {
+ if (item == null)
+ throw new IllegalArgumentException("Item is null.");
+
+ if (index < 0 || index >= items.size())
+ throw new IllegalArgumentException("Index " + index
+ + " out of bounds.");
+
+ items.add(index, item);
+ fireIntervalAdded(this, index, index);
+ }
+
+ /**
+ * Adds a new item to the end of the channel.
+ *
+ * @param item
+ * The new item to add.
+ */
+ public void add(ShowItem item) {
+ if (item == null)
+ throw new IllegalArgumentException("Item is null.");
+
+ items.add(item);
+ fireIntervalAdded(this, items.size() - 1, items.size() - 1);
+ }
+
+ /**
+ * Retrieves an item from the channel.
+ *
+ * @param index
+ * The index of the item to retrieve from the channel.
+ *
+ * @return the item at the given index in the list.
+ *
+ * @throws IllegalArgumentException
+ * if the index is negative or overflowing.
+ */
+ public ShowItem get(int index) {
+ if (index < 0 || index >= items.size())
+ throw new IllegalArgumentException("Index " + index
+ + " out of bounds.");
+
+ return items.get(index);
+ }
+
+ /**
+ * List model retrieval wrapper for get.
+ *
+ * @param index
+ * The index of the item to retrieve from the channel.
+ *
+ * @return the item at the given index in the list.
+ */
+ @Override
+ public Object getElementAt(int index) {
+ return get(index);
+ }
+
+ /**
+ * @return the size of the list.
+ */
+ @Override
+ public int getSize() {
+ return items.size();
+ }
+}
diff --git a/src/uk/org/ury/common/show/ShowUtils.java b/src/uk/org/ury/common/show/ShowUtils.java
new file mode 100644
index 0000000..a806d8f
--- /dev/null
+++ b/src/uk/org/ury/common/show/ShowUtils.java
@@ -0,0 +1,190 @@
+/*
+ * ShowUtils.java
+ * ------------------
+ *
+ * Part of the URY Backend Platform
+ *
+ * V0.00 2011/03/21
+ *
+ * (C) 2011 URY Computing
+ */
+
+package uk.org.ury.common.show;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import uk.org.ury.backend.database.DatabaseDriver;
+import uk.org.ury.backend.database.exceptions.QueryFailureException;
+import uk.org.ury.common.show.item.ShowItem;
+import uk.org.ury.common.show.item.ShowItemProperty;
+
+
+/**
+ * A set of common utility routines to facilitate the extraction of show items
+ * from the show storage areas of the URY database.
+ *
+ * @author Matt Windsor
+ */
+public class ShowUtils {
+ /**
+ * The number of channels reserved for show items.
+ *
+ * TODO: move this somewhere more appropriate.
+ */
+ public static final int NUM_CHANNELS = 3;
+
+ // Maximum number of results to pull from database
+ private static final int MAX_RESULTS = 50;
+
+ // Resource bundle (for exception reasons)
+ private static ResourceBundle rb = ResourceBundle.getBundle(ShowUtils.class
+ .getPackage().getName() + ".ShowUtils");
+
+ /**
+ * Return the names of the public track folders, or "bins".
+ *
+ * @param db
+ * The database to query.
+ *
+ * @return a list of the public folder names. The list may be empty.
+ *
+ * @throws IllegalArgumentException
+ * if the database is null, the show ID is negative or the
+ * channel index falls out of bounds.
+ *
+ * @throws QueryFailureException
+ * if the database backend yielded an error while executing the
+ * search query.
+ */
+ public static List<String> getPublicFolders(DatabaseDriver db)
+ throws QueryFailureException {
+ if (db == null)
+ throw new IllegalArgumentException(
+ rb.getString("ERR_DB_HANDLE_IS_NULL"));
+
+ List<String> results = new ArrayList<String>();
+
+ ResultSet rs = null;
+
+ try {
+ rs = db.executeQuery("SELECT share AS name, description"
+ + " FROM baps_filefolder" + " WHERE baps_filefolder.public"
+ + " = TRUE" + " ORDER BY filefolderid ASC", MAX_RESULTS);
+ } catch (SQLException e1) {
+ throw new QueryFailureException(e1.getMessage());
+ }
+
+ try {
+ while (rs.next()) {
+ results.add(rs.getString(2));
+ }
+ } catch (SQLException e) {
+ throw new QueryFailureException(e.getMessage());
+ }
+
+ return results;
+ }
+
+ /**
+ * Given a show and a channel, retrieve a list of all show items bound to
+ * that channel for the show.
+ *
+ * @param db
+ * The database to query.
+ *
+ * @param showID
+ * The unique number that identifies the show.
+ *
+ * @param channel
+ * The index of the channel to query.
+ *
+ * @return a list of ShowItems extracted from the show and channel. The list
+ * may be empty.
+ *
+ * @throws IllegalArgumentException
+ * if the database is null, the show ID is negative or the
+ * channel index falls out of bounds.
+ *
+ * @throws QueryFailureException
+ * if the database backend yielded an error while executing the
+ * search query.
+ */
+ public static List<ShowItem> getChannelList(DatabaseDriver db, int showID,
+ int channel) throws QueryFailureException {
+ if (db == null)
+ throw new IllegalArgumentException(
+ rb.getString("ERR_DB_HANDLE_IS_NULL"));
+
+ if (showID < 0)
+ throw new IllegalArgumentException(
+ rb.getString("ERR_NEGATIVE_SHOW_ID"));
+
+ if (channel < 0 || channel >= NUM_CHANNELS)
+ throw new IllegalArgumentException(
+ rb.getString("ERR_CH_OUT_OF_BOUNDS"));
+
+ List<ShowItem> results = new ArrayList<ShowItem>();
+
+ ResultSet rs = null;
+
+ Object[] params = { showID, channel };
+
+ try {
+ rs = db.executeQuery("SELECT name1, name2, position"
+ + " FROM baps_show" + " INNER JOIN baps_listing"
+ + " ON baps_show.showid"
+ + " = baps_listing.showid"
+ + " INNER JOIN baps_item"
+ + " ON baps_listing.listingid"
+ + " = baps_item.listingid"
+ + " WHERE baps_show.showid"
+ + " = ?"
+ + " AND baps_listing.channel"
+ + " = ?"
+ + " ORDER BY position ASC", params, MAX_RESULTS);
+ } catch (SQLException e) {
+ throw new QueryFailureException(e.getMessage());
+ }
+
+ try {
+ while (rs.next()) {
+ results.add(translateRow(rs));
+ }
+ } catch (SQLException e) {
+ throw new QueryFailureException(e.getMessage());
+ }
+ return results;
+ }
+
+ /**
+ * Translate a row retrieved from the database into a ShowItem.
+ *
+ * @param rs
+ * The result-set, or database cursor, pointing to the row to
+ * translate.
+ *
+ * @return A new ShowItem containing the properties extracted from the
+ * translated row.
+ */
+ private static ShowItem translateRow(ResultSet rs) {
+ // Translate SQL columns into a list of properties.
+
+ HashMap<ShowItemProperty, String> properties = new HashMap<ShowItemProperty, String>();
+
+ for (ShowItemProperty p : ShowItemProperty.values()) {
+ try {
+ properties.put(p, rs.getString(p.sql));
+ } catch (SQLException e) {
+ // Ignore this, as it is almost certainly just a non-existent
+ // property.
+ }
+ }
+
+ return new ShowItem(properties);
+ }
+} \ No newline at end of file
diff --git a/src/uk/org/ury/common/show/ShowUtils.properties b/src/uk/org/ury/common/show/ShowUtils.properties
new file mode 100644
index 0000000..7f8a2fa
--- /dev/null
+++ b/src/uk/org/ury/common/show/ShowUtils.properties
@@ -0,0 +1,4 @@
+// Exceptions
+ERR_DB_HANDLE_IS_NULL = "Database handle is null."
+ERR_NEGATIVE_SHOW_ID = "Show ID is negative."
+ERR_CH_OUT_OF_BOUNDS = Channel index is out of bounds." \ No newline at end of file
diff --git a/src/uk/org/ury/common/show/item/ShowItem.java b/src/uk/org/ury/common/show/item/ShowItem.java
new file mode 100644
index 0000000..d1d1be1
--- /dev/null
+++ b/src/uk/org/ury/common/show/item/ShowItem.java
@@ -0,0 +1,68 @@
+/**
+ *
+ */
+package uk.org.ury.common.show.item;
+
+
+import java.util.Map;
+
+import uk.org.ury.backend.database.DatabaseItem;
+import uk.org.ury.backend.database.exceptions.MissingPropertyException;
+
+
+/**
+ * An item in the show database.
+ *
+ * @author Matt Windsor
+ */
+
+public class ShowItem extends DatabaseItem<ShowItemProperty, String>
+{
+ /**
+ * Construct a new ShowItem.
+ *
+ * @param properties The map of properties to store in the show item.
+ */
+
+ public
+ ShowItem (Map<ShowItemProperty, String> properties)
+ {
+ super (properties);
+ }
+
+
+ /**
+ * @return a string representation of the ShowItem.
+ */
+
+ @Override
+ public String
+ toString ()
+ {
+ String name1;
+ String name2;
+
+ try
+ {
+ name1 = get (ShowItemProperty.NAME1);
+ }
+ catch (MissingPropertyException e1)
+ {
+ name1 = "Unknown";
+ }
+
+ try
+ {
+ name2 = get (ShowItemProperty.NAME2);
+ }
+ catch (MissingPropertyException e2)
+ {
+ name2 = null;
+ }
+
+ if (name2 != null)
+ return name1 + " - " + name2;
+ else
+ return name1;
+ }
+}
diff --git a/src/uk/org/ury/common/show/item/ShowItemProperty.java b/src/uk/org/ury/common/show/item/ShowItemProperty.java
new file mode 100644
index 0000000..43d4e99
--- /dev/null
+++ b/src/uk/org/ury/common/show/item/ShowItemProperty.java
@@ -0,0 +1,26 @@
+package uk.org.ury.common.show.item;
+
+
+/**
+ * Enumeration of the parameters that are stored in a ShowItem.
+ *
+ * @author Matt Windsor
+ */
+
+public enum ShowItemProperty
+ {
+ // Constant SQL identifier
+ NAME1 ("name1"),
+ NAME2 ("name2"),
+ POSITION ("positionid");
+
+
+ public final String sql;
+
+
+ private
+ ShowItemProperty (String sql)
+ {
+ this.sql = sql;
+ }
+ }; \ No newline at end of file
diff --git a/src/uk/org/ury/common/show/item/package.html b/src/uk/org/ury/common/show/item/package.html
new file mode 100644
index 0000000..0b2af5a
--- /dev/null
+++ b/src/uk/org/ury/common/show/item/package.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+
+<HTML>
+ <HEAD>
+ <TITLE>uk.org.ury.show.item</TITLE>
+ </HEAD>
+ <BODY>
+ <P>The show item class and related properties.</P>
+ <P>The two contained classes, ShowItem and ShowItemProperty,
+ implement the storage of items (songs, audio tracks, etc) that
+ can be placed in show channels.</P>
+ </BODY>
+</HTML> \ No newline at end of file