package songbook.song;
import songbook.server.ChannelUtil;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.text.Normalizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
public class SongDatabase {
public static String SONG_EXTENSION = ".song";
private final Logger logger = Logger.getLogger("Songbook");
private Path songDir;
public SongDatabase(Path songDir) throws IOException {
this.songDir = songDir;
if (Files.exists(songDir) == false) {
Files.createDirectories(songDir);
}
}
public void clearCache() {
// TODO to implement when a cache will be needed.
}
public Stream<String> listSongIds() {
try {
return Files.list(songDir).map(SongDatabase::extractId);
} catch (IOException e) {
logger.log(Level.SEVERE, "Can't list songs", e);
return Stream.empty();
}
}
public ReadableByteChannel readChannelForSong(String id) {
try {
return Files.newByteChannel(getSongPath(id));
} catch (IOException e) {
logger.log(Level.SEVERE, "Can't read song '" + id + "'", e);
return null;
}
}
public String getSongContents(String id) {
try {
ReadableByteChannel channel = readChannelForSong(id);
return channel == null ? null : ChannelUtil.getStringContents(channel);
} catch (IOException e) {
logger.log(Level.SEVERE, "Can't read song '" + id + "'", e);
return null;
}
}
public WritableByteChannel writeChannelForSong(String id) {
try {
Path path = getSongPath(id);
if (Files.exists(path) == false) {
Files.createDirectories(path.getParent());
}
return Files.newByteChannel(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC);
} catch (IOException e) {
logger.log(Level.SEVERE, "Can't write or create song '" + id + "'", e);
return null;
}
}
public boolean delete(String id) {
try {
Files.delete(getSongPath(id));
return true;
} catch (IOException e) {
logger.log(Level.SEVERE, "Can't delete song '" + id + "'", e);
return false;
}
}
/** Verify if song exists */
public boolean exists(String id) {
return Files.exists(getSongPath(id));
}
/**
* Generate a clean id using title and artist information.
* @param title
* @param artist
* @return
*/
public String generateId(String title, String artist) {
String id = encodeId(artist + "-" + title);
int i = 1;
while (exists(id)) {
id = encodeId(artist + "-" + title + "_" + i);
i++;
}
return id;
}
/**
* Is this filePath correspond to a song file testing
* @param filePath
* @return
*/
private static boolean isSong(Path filePath) {
return Files.isRegularFile(filePath) && filePath.toString().endsWith(SONG_EXTENSION);
}
/**
* Assuming that file ending with '.song' extension
* @param songPath
* @return
*/
private static String extractId(Path songPath) {
String filename = songPath.getFileName().toString();
return filename.substring(0, filename.length() - SONG_EXTENSION.length());
}
private Path getSongPath(String id) {
return songDir.resolve(id + SONG_EXTENSION);
}
private static String encodeId(String id) {
try {
id = id.replace("'", " ").replace("\"", " ").trim();
id = Normalizer.normalize(id, Normalizer.Form.NFD);
id = id.replaceAll("\\p{M}", "").toLowerCase();
return URLEncoder.encode(id, "UTF-8");
} catch (UnsupportedEncodingException e) {
// do nothing
return id;
}
}
}