2 * DemosceneMusic - DataManager.java - Copyright © 2012 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.demoscenemusic.data;
20 import java.sql.ResultSet;
21 import java.sql.SQLException;
22 import java.util.Collection;
23 import java.util.EnumMap;
24 import java.util.HashSet;
25 import java.util.List;
27 import java.util.concurrent.Callable;
29 import net.pterodactylus.demoscenemusic.data.Track.Relationship;
30 import net.pterodactylus.util.collection.Memoizer;
31 import net.pterodactylus.util.database.Database;
32 import net.pterodactylus.util.database.DatabaseException;
33 import net.pterodactylus.util.database.Field;
34 import net.pterodactylus.util.database.Join;
35 import net.pterodactylus.util.database.Join.JoinType;
36 import net.pterodactylus.util.database.ObjectCreator;
37 import net.pterodactylus.util.database.ObjectCreator.StringCreator;
38 import net.pterodactylus.util.database.OrderField;
39 import net.pterodactylus.util.database.Parameter.StringParameter;
40 import net.pterodactylus.util.database.Query;
41 import net.pterodactylus.util.database.Query.Type;
42 import net.pterodactylus.util.database.ResultProcessor;
43 import net.pterodactylus.util.database.ValueField;
44 import net.pterodactylus.util.database.ValueFieldWhereClause;
47 * Interface between the database and the application.
49 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
51 public class DataManager {
53 /** The artist object creator. */
54 @SuppressWarnings("synthetic-access")
55 private final ObjectCreator<Artist> artistCreator = new ArtistCreator();
57 /** The group object creator. */
58 @SuppressWarnings("synthetic-access")
59 private final ObjectCreator<Group> groupCreator = new GroupCreator();
61 /** The track object creator. */
62 @SuppressWarnings("synthetic-access")
63 private final ObjectCreator<Track> trackCreator = new TrackCreator();
65 /** The style object creator. */
66 @SuppressWarnings("synthetic-access")
67 private final ObjectCreator<Style> styleCreator = new StyleCreator();
69 /** The {@link User} object creator. */
70 @SuppressWarnings("synthetic-access")
71 private final ObjectCreator<User> userCreator = new UserCreator();
74 private final Database database;
77 * Creates a new data manager.
80 * The database to operate on
82 public DataManager(Database database) {
83 this.database = database;
87 * Returns all artists.
90 * @throws DatabaseException
91 * if a database error occurs
93 public Collection<Artist> getAllArtists() throws DatabaseException {
94 Query query = new Query(Type.SELECT, "ARTISTS");
95 query.addField(new Field("ARTISTS.*"));
96 return database.getMultiple(query, artistCreator);
100 * Returns the artist with the given ID.
103 * The ID of the artist
104 * @return The artist with the given ID, or {@code null} if there is no
105 * artist with the given ID
106 * @throws DatabaseException
107 * if a database error occurs
109 public Artist getArtistById(String id) throws DatabaseException {
110 Query query = new Query(Type.SELECT, "ARTISTS");
111 query.addField(new Field("ARTISTS.*"));
112 query.addWhereClause(new ValueFieldWhereClause(new ValueField("ARTISTS.ID", new StringParameter(id))));
113 return database.getSingle(query, artistCreator);
117 * Returns all artists that belong to the group with the given ID.
120 * The ID of the group
121 * @return All artists belonging to the given group
122 * @throws DatabaseException
123 * if a database error occurs
125 public Collection<Artist> getArtistsByGroup(String groupId) throws DatabaseException {
126 Query query = new Query(Type.SELECT, "ARTISTS");
127 query.addField(new Field("ARTISTS.*"));
128 query.addJoin(new Join(JoinType.INNER, "GROUP_ARTISTS", new Field("ARTISTS.ID"), new Field("GROUP_ARTISTS.ARTIST")));
129 query.addWhereClause(new ValueFieldWhereClause(new ValueField("GROUP_ARTISTS.GROUP_", new StringParameter(groupId))));
130 return database.getMultiple(query, artistCreator);
134 * Returns all artists involved in the track with the given ID.
137 * The ID of the track
138 * @return All artists involved in the track, in preferred order
139 * @throws DatabaseException
140 * if a database error occurs
142 public List<Artist> getArtistsByTrack(String trackId) throws DatabaseException {
143 Query query = new Query(Type.SELECT, "ARTISTS");
144 query.addField(new Field("ARTISTS.*"));
145 query.addJoin(new Join(JoinType.INNER, "TRACK_ARTISTS", new Field("TRACK_ARTISTS.ARTIST"), new Field("ARTISTS.ID")));
146 query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_ARTISTS.TRACK", new StringParameter(trackId))));
147 query.addOrderField(new OrderField(new Field("TRACK_ARTISTS.DISPLAY_ORDER")));
148 return database.getMultiple(query, artistCreator);
152 * Returns all remix artists involved in the track with the given ID.
155 * The ID of the track
156 * @return All remix artists involved in the track, in preferred order
157 * @throws DatabaseException
158 * if a database error occurs
160 public List<Artist> getRemixArtistsByTrack(String trackId) throws DatabaseException {
161 Query query = new Query(Type.SELECT, "ARTISTS");
162 query.addField(new Field("ARTISTS.*"));
163 query.addJoin(new Join(JoinType.INNER, "TRACK_REMIX_ARTISTS", new Field("TRACK_REMIX_ARTISTS.ARTIST"), new Field("ARTISTS.ID")));
164 query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_REMIX_ARTISTS.TRACK", new StringParameter(trackId))));
165 query.addOrderField(new OrderField(new Field("TRACK_REMIX_ARTISTS.DISPLAY_ORDER")));
166 return database.getMultiple(query, artistCreator);
170 * Returns all related tracks for the track with the given ID.
173 * The ID of the tracks
174 * @return A mapping from relationship to all tracks that match the relation
175 * @throws DatabaseException
176 * if a database error occurs
178 public Map<Relationship, Collection<Track>> getRelatedTracksByTrack(String trackId) throws DatabaseException {
179 Query query = new Query(Type.SELECT, "TRACKS");
180 query.addField(new Field("TRACKS.*"));
181 query.addField(new Field("TRACK_RELATIONS.*"));
182 query.addJoin(new Join(JoinType.INNER, "TRACK_RELATIONS", new Field("TRACK_RELATIONS.TRACK"), new Field("TRACK_RELATIONS.RELATED_TRACK")));
183 query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_RELATIONS.TRACK", new StringParameter(trackId))));
184 final Map<Relationship, Collection<Track>> relatedTracks = new EnumMap<Relationship, Collection<Track>>(Relationship.class);
185 database.process(query, new ResultProcessor() {
188 @SuppressWarnings("synthetic-access")
189 public void processResult(ResultSet resultSet) throws SQLException {
190 Track track = trackCreator.createObject(resultSet);
191 Relationship relationship = Relationship.valueOf(resultSet.getString("TRACK_RELATIONS.RELATIONSHIP"));
192 if (!relatedTracks.containsKey(relationship)) {
193 relatedTracks.put(relationship, new HashSet<Track>());
195 relatedTracks.get(relationship).add(track);
198 return relatedTracks;
202 * Returns the track with the given ID.
205 * The ID of the track
206 * @return The track with the given ID, or {@code null} if there is no such
208 * @throws DatabaseException
209 * if a database error occurs
211 public Track getTrackById(String id) throws DatabaseException {
212 Query query = new Query(Type.SELECT, "TRACKS");
213 query.addField(new Field("TRACKS.*"));
214 query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACKS.ID", new StringParameter(id))));
215 return database.getSingle(query, trackCreator);
219 * Returns all tracks by the artist with the given ID.
222 * The ID of the artist
223 * @return All tracks by the given artist
224 * @throws DatabaseException
225 * if a database error occurs
227 public Collection<Track> getTracksByArtist(String artistId) throws DatabaseException {
228 Query query = new Query(Type.SELECT, "TRACKS");
229 query.addField(new Field("TRACKS.*"));
230 query.addJoin(new Join(JoinType.INNER, "TRACK_ARTISTS", new Field("TRACKS.ID"), new Field("TRACK_ARTISTS.TRACK")));
231 query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_ARTISTS.ARTIST", new StringParameter(artistId))));
232 return database.getMultiple(query, trackCreator);
236 * Returns all groups the artist with the given ID belongs to.
239 * The ID of the artist
240 * @return All groups the artist belongs to
241 * @throws DatabaseException
242 * if a database error occurs
244 public Collection<Group> getGroupsByArtist(String artistId) throws DatabaseException {
245 Query query = new Query(Type.SELECT, "GROUPS");
246 query.addField(new Field("GROUPS.*"));
247 query.addJoin(new Join(JoinType.INNER, "GROUP_ARTISTS", new Field("GROUPS.ID"), new Field("GROUP_ARTISTS.GROUP_")));
248 query.addWhereClause(new ValueFieldWhereClause(new ValueField("GROUP_ARTISTS.ARTIST", new StringParameter(artistId))));
249 return database.getMultiple(query, groupCreator);
253 * Returns all styles for the track with the given ID.
256 * The ID of the track
257 * @return All styles for the given track
258 * @throws DatabaseException
259 * if a database error occurs
261 public Collection<Style> getStylesByTrack(String trackId) throws DatabaseException {
262 Query query = new Query(Type.SELECT, "STYLES");
263 query.addField(new Field("STYLES.*"));
264 query.addJoin(new Join(JoinType.INNER, "TRACK_STYLES", new Field("STYLES.ID"), new Field("TRACK_STYLES.STYLE")));
265 query.addWhereClause(new ValueFieldWhereClause(new ValueField("TRACK_STYLES.TRACK", new StringParameter(trackId))));
266 return database.getMultiple(query, styleCreator);
270 * Returns the user connected with the given OpenID.
273 * The OpenID to find the user for
274 * @return The user connected with the given OpenID, or {@code null} if
275 * there is no such user
276 * @throws DatabaseException
277 * if a database error occurs
279 public User getUserByOpenId(String openId) throws DatabaseException {
280 Query query = new Query(Type.SELECT, "USERS");
281 query.addField(new Field("USERS.*"));
282 query.addJoin(new Join(JoinType.INNER, "USER_OPENIDS", new Field("USER_OPENIDS.USER"), new Field("USERS.ID")));
283 query.addWhereClause(new ValueFieldWhereClause(new ValueField("USER_OPENIDS.OPENID", new StringParameter(openId))));
284 return database.getSingle(query, userCreator);
288 * Returns all OpenIDs connected with the user with the given ID.
292 * @return All OpenIDs connected with the given user
293 * @throws DatabaseException
294 * if a database error occurs
296 public Collection<String> getOpenIdsByUser(String userId) throws DatabaseException {
297 Query query = new Query(Type.SELECT, "USER_OPENIDS");
298 query.addField(new Field("USER_OPENIDS.*"));
299 query.addWhereClause(new ValueFieldWhereClause(new ValueField("USER_OPENIDS.USER", new StringParameter(userId))));
300 return database.getMultiple(query, new StringCreator("USER_OPENIDS.OPENID"));
304 * {@link Artist} implementation that retrieves some attributes (such as
305 * {@link #getGroups()}, and {@link #getTracks()}) from the
306 * {@link DataManager} on demand.
308 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
310 private class LazyArtist extends DefaultArtist {
312 /** Memoizer for the tracks by this artist. */
313 private final Memoizer<Void> tracksMemoizer = new Memoizer<Void>(new Callable<Void>() {
315 public Void call() throws DatabaseException {
316 if (!hasValue("tracks")) {
317 getValue("tracks", Collection.class).set(getTracksByArtist(getId()));
323 /** Memoizer for the groups of this artist. */
324 private final Memoizer<Void> groupsMemoizer = new Memoizer<Void>(new Callable<Void>() {
327 public Void call() throws Exception {
328 if (!hasValue("groups")) {
329 getValue("groups", Collection.class).set(getGroupsByArtist(getId()));
337 * Creates a new lazy artist.
340 * The ID of the artist
342 public LazyArtist(String id) {
347 // DEFAULTARTIST METHODS
354 public Collection<Group> getGroups() {
355 groupsMemoizer.get();
356 return super.getGroups();
363 public Collection<Track> getTracks() {
364 tracksMemoizer.get();
365 return super.getTracks();
371 * {@link ObjectCreator} implementation that can create {@link Artist}
372 * objects. This specific class actually creates {@link LazyArtist}
375 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
377 private class ArtistCreator implements ObjectCreator<Artist> {
383 public Artist createObject(ResultSet resultSet) throws SQLException {
384 return new LazyArtist(resultSet.getString("ARTISTS.ID")).setName(resultSet.getString("ARTISTS.NAME"));
390 * {@link Group} implementation that retrieves some attributes (such as
391 * {@link #getArtists()}) from the {@link DataManager} on demand.
393 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
395 private class LazyGroup extends DefaultGroup {
397 /** Memoizer for the artist. */
398 private final Memoizer<Void> artistsMemoizer = new Memoizer<Void>(new Callable<Void>() {
401 public Void call() throws Exception {
402 if (!hasValue("artists")) {
403 getValue("artists", Collection.class).set(getArtistsByGroup(getId()));
411 * Creates a new lazy group.
414 * The ID of the group
416 public LazyGroup(String id) {
421 // DEFAULTGROUP METHODS
428 public Collection<Artist> getArtists() {
429 artistsMemoizer.get();
430 return super.getArtists();
436 * {@link ObjectCreator} implementation that can create {@link Group}
437 * objects. This specific implementation creates {@link LazyGroup}
440 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
442 private class GroupCreator implements ObjectCreator<Group> {
448 public Group createObject(ResultSet resultSet) throws SQLException {
449 return new LazyGroup(resultSet.getString("GROUPS.ID")).setName(resultSet.getString("GROUPS.NAME")).setUrl(resultSet.getString("GROUPS.URL"));
455 * {@link Track} implementation that retrieves some attributes (such as
456 * {@link #getArtists()}, and {@link #getStyles()}) from the
457 * {@link DataManager} on demand.
459 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
461 private class LazyTrack extends DefaultTrack {
463 /** Memoizer for the artists. */
464 private final Memoizer<Void> artistsMemoizer = new Memoizer<Void>(new Callable<Void>() {
467 public Void call() throws Exception {
468 if (!hasValue("artists")) {
469 getValue("artists", List.class).set(getArtistsByTrack(getId()));
476 /** Memoizer for the styles. */
477 private final Memoizer<Void> stylesMemoizer = new Memoizer<Void>(new Callable<Void>() {
480 public Void call() throws Exception {
481 if (!hasValue("styles")) {
482 getValue("styles", Collection.class).set(getStylesByTrack(getId()));
489 /** Memoizer for the remix artists. */
490 private final Memoizer<Void> remixArtistsMemoizer = new Memoizer<Void>(new Callable<Void>() {
493 public Void call() throws Exception {
494 if (!hasValue("remixArtists")) {
495 getValue("remixArtists", List.class).set(getRemixArtistsByTrack(getId()));
502 /** Memoizer for the related tracks. */
503 private final Memoizer<Void> relatedTracksMemoizer = new Memoizer<Void>(new Callable<Void>() {
506 public Void call() throws Exception {
507 if (!hasValue("relatedTracks")) {
508 getValue("relatedTracks", Map.class).set(getRelatedTracksByTrack(getId()));
515 * Creates a new track.
518 * The ID of the track
520 public LazyTrack(String id) {
525 // DEFAULTTRACK METHODS
532 public List<Artist> getArtists() {
533 artistsMemoizer.get();
534 return super.getArtists();
541 public Collection<Style> getStyles() {
542 stylesMemoizer.get();
543 return super.getStyles();
550 public List<Artist> getRemixArtists() {
551 remixArtistsMemoizer.get();
552 return super.getRemixArtists();
559 public Map<Relationship, Collection<Track>> getRelatedTracks() {
560 relatedTracksMemoizer.get();
561 return super.getRelatedTracks();
567 * {@link ObjectCreator} implementation that can create {@link Track}
568 * objects. This specific implementation creates {@link LazyTrack}
571 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
573 private class TrackCreator implements ObjectCreator<Track> {
579 public Track createObject(ResultSet resultSet) throws SQLException {
580 return new LazyTrack(resultSet.getString("TRACKS.ID")).setName(resultSet.getString("TRACKS.NAME")).setRemix(resultSet.getString("TRACKS.REMIX"));
586 * {@link ObjectCreator} implementation that can create {@link Style}
589 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
591 private class StyleCreator implements ObjectCreator<Style> {
597 public Style createObject(ResultSet resultSet) throws SQLException {
598 return new DefaultStyle(resultSet.getString("STYLES.ID")).setName(resultSet.getString("STYLES.NAME"));
604 * {@link User} implementation that retrieves some attributes (such as
605 * {@link #getOpenIds()}) from the {@link DataManager} on demand.
607 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
609 private class LazyUser extends DefaultUser {
611 /** Memoizer for a user’s OpenIDs. */
612 private final Memoizer<Void> openIdMemoizer = new Memoizer<Void>(new Callable<Void>() {
615 public Void call() throws Exception {
616 if (!hasValue("openIds")) {
617 getValue("openIds", Collection.class).set(getOpenIdsByUser(getId()));
624 * Creates a new user.
629 public LazyUser(String id) {
637 public Collection<String> getOpenIds() {
638 openIdMemoizer.get();
639 return super.getOpenIds();
645 * {@link ObjectCreator} implementation that can create {@link User}
646 * objects. This specific implementation creates {@link LazyUser} instances.
648 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
650 private class UserCreator implements ObjectCreator<User> {
656 public User createObject(ResultSet resultSet) throws SQLException {
657 return new LazyUser(resultSet.getString("USERS.ID")).setName(resultSet.getString("USERS.NAME")).setPasswordHash(resultSet.getString("USERS.PASSWORD")).setLevel(resultSet.getInt("USERS.LEVEL"));