+ /**
+ * Returns the derivative with the given ID.
+ *
+ * @param id
+ * The ID of the derivatives to load
+ * @return The derivative with the given ID
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public TrackDerivative getTrackDerivativeById(String id) throws DatabaseException {
+ Query query = new Query(Type.SELECT, "TRACK_DERIVATIVES");
+ query.addField(new Field("TRACK_DERIVATIVES.*"));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_DERIVATIVES.ID", new StringParameter(id))));
+ return loadTrackDerivativeProperties(database.getSingle(query, trackDerivativeCreator));
+ }
+
+ /**
+ * Returns the derivatives for the given track.
+ *
+ * @param track
+ * The track to get the derivatives for
+ * @return The derivatives for the given track
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public Collection<TrackDerivative> getTrackDerivativesByTrack(Track track) throws DatabaseException {
+ Query query = new Query(Type.SELECT, "TRACK_DERIVATIVES");
+ query.addField(new Field("TRACK_DERIVATIVES.*"));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_DERIVATIVES.TRACK", new StringParameter(track.getId()))));
+ return loadTrackDerivativeProperties(database.getMultiple(query, trackDerivativeCreator));
+ }
+
+ /**
+ * Loads the properties for the given track derivative.
+ *
+ * @param trackDerivative
+ * The track derivative to load the properties for
+ * @return The track derivative with its properties loaded
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public TrackDerivative loadTrackDerivativeProperties(TrackDerivative trackDerivative) throws DatabaseException {
+ return loadProperties(trackDerivative, "TRACK_DERIVATIVE_PROPERTIES", "TRACK_DERIVATIVE");
+ }
+
+ /**
+ * Loads the properties for the given track derivatives.
+ *
+ * @param trackDerivatives
+ * The track derivatives to load the properties for
+ * @return The track derivatives with their properties loaded
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public List<TrackDerivative> loadTrackDerivativeProperties(List<TrackDerivative> trackDerivatives) throws DatabaseException {
+ for (TrackDerivative trackDerivative : trackDerivatives) {
+ loadTrackDerivativeProperties(trackDerivative);
+ }
+ return trackDerivatives;
+ }
+
+ /**
+ * Returns all groups the artist with the given ID belongs to.
+ *
+ * @param artistId
+ * The ID of the artist
+ * @return All groups the artist belongs to
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public Collection<Group> getGroupsByArtist(String artistId) throws DatabaseException {
+ Query query = new Query(Type.SELECT, "GROUPS");
+ query.addField(new Field("GROUPS.*"));
+ query.addJoin(new Join(JoinType.INNER, "GROUP_ARTISTS", new Field("GROUPS.ID"), new Field("GROUP_ARTISTS.GROUP_")));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField("GROUP_ARTISTS.ARTIST", new StringParameter(artistId))));
+ return database.getMultiple(query, groupCreator);
+ }
+
+ /**
+ * Returns all styles for the track with the given ID.
+ *
+ * @param trackId
+ * The ID of the track
+ * @return All styles for the given track
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public Collection<Style> getStylesByTrack(String trackId) throws DatabaseException {
+ Query query = new Query(Type.SELECT, "STYLES");
+ query.addField(new Field("STYLES.*"));
+ query.addJoin(new Join(JoinType.INNER, "TRACK_STYLES", new Field("STYLES.ID"), new Field("TRACK_STYLES.STYLE")));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_STYLES.TRACK", new StringParameter(trackId))));
+ return database.getMultiple(query, styleCreator);
+ }
+
+ /**
+ * Returns the user with the given name.
+ *
+ * @param username
+ * The name of the user
+ * @return The user, or {@code null} if the user does not exist
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public User getUserByName(String username) throws DatabaseException {
+ Query query = new Query(Type.SELECT, "USERS");
+ query.addField(new Field("USERS.*"));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField("USERS.NAME", new StringParameter(username))));
+ return database.getSingle(query, userCreator);
+ }
+
+ /**
+ * Returns the user connected with the given OpenID.
+ *
+ * @param openId
+ * The OpenID to find the user for
+ * @return The user connected with the given OpenID, or {@code null} if
+ * there is no such user
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public User getUserByOpenId(String openId) throws DatabaseException {
+ Query query = new Query(Type.SELECT, "USERS");
+ query.addField(new Field("USERS.*"));
+ query.addJoin(new Join(JoinType.INNER, "USER_OPENIDS", new Field("USER_OPENIDS.USER"), new Field("USERS.ID")));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField("USER_OPENIDS.OPENID", new StringParameter(openId))));
+ return database.getSingle(query, userCreator);
+ }
+
+ /**
+ * Returns all OpenIDs connected with the user with the given ID.
+ *
+ * @param userId
+ * The ID of the user
+ * @return All OpenIDs connected with the given user
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ public Collection<String> getOpenIdsByUser(String userId) throws DatabaseException {
+ Query query = new Query(Type.SELECT, "USER_OPENIDS");
+ query.addField(new Field("USER_OPENIDS.*"));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField("USER_OPENIDS.USER", new StringParameter(userId))));
+ return database.getMultiple(query, new StringCreator("USER_OPENIDS.OPENID"));
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Loads the properties for the given object.
+ *
+ * @param <T>
+ * The type of the object
+ * @param object
+ * The object
+ * @param table
+ * The table to load the properties from
+ * @param type
+ * The type of the object (“ARTIST,” “TRACK,” etc.)
+ * @return The object with its properties loaded
+ * @throws DatabaseException
+ * if a database error occurs
+ */
+ private <T extends Base> T loadProperties(final T object, final String table, String type) throws DatabaseException {
+ Query query = new Query(Type.SELECT, table);
+ query.addField(new Field(table + ".PROPERTY"));
+ query.addField(new Field(table + ".VALUE"));
+ query.addWhereClause(new ValueFieldWhereClause(new ValueField(type, new StringParameter(object.getId()))));
+ database.process(query, new ResultProcessor() {
+
+ @Override
+ public void processResult(ResultSet resultSet) throws SQLException {
+ if (resultSet.isFirst()) {
+ object.getProperties().removeAll();
+ }
+ object.getProperties().set(resultSet.getString(table + ".PROPERTY"), resultSet.getString(table + ".VALUE"));
+ }
+
+ });
+ return object;
+ }
+
+ /**
+ * {@link Artist} implementation that retrieves some attributes (such as
+ * {@link #getGroups()}, and {@link #getTracks()}) from the
+ * {@link DataManager} on demand.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ private class LazyArtist extends DefaultArtist {
+
+ /** Memoizer for the tracks by this artist. */
+ private final Memoizer<Void> tracksMemoizer = new Memoizer<Void>(new Callable<Void>() {
+ @Override
+ public Void call() throws DatabaseException {
+ if (!hasValue("tracks")) {
+ getValue("tracks", Collection.class).set(getTracksByArtist(getId()));
+ }
+ return null;
+ }
+ });
+
+ /** Memoizer for the groups of this artist. */
+ private final Memoizer<Void> groupsMemoizer = new Memoizer<Void>(new Callable<Void>() {
+
+ @Override
+ public Void call() throws Exception {
+ if (!hasValue("groups")) {
+ getValue("groups", Collection.class).set(getGroupsByArtist(getId()));
+ }
+ return null;
+ }
+
+ });
+
+ /**
+ * Creates a new lazy artist.
+ *
+ * @param id
+ * The ID of the artist
+ */
+ public LazyArtist(String id) {
+ super(id);
+ }
+
+ //
+ // DEFAULTARTIST METHODS
+ //