<commit_before>package org.praisenter.data.slide;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;

import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.praisenter.LockMap;
import org.praisenter.data.DataFormatProvider;
import org.praisenter.data.DataImportResult;
import org.praisenter.data.DataReadResult;
import org.praisenter.data.KnownFormat;
import org.praisenter.data.PersistAdapter;
import org.praisenter.data.PraisenterFormatProvider;
import org.praisenter.data.UnknownFormatException;
import org.praisenter.data.json.JsonIO;
import org.praisenter.utility.MimeType;
import org.praisenter.utility.Streams;

public final class SlidePersistAdapter implements PersistAdapter<Slide> {
	private static final Logger LOGGER = LogManager.getLogger();
	private static final String EXTENSION = "json";
	
	private final SlideConfiguration configuration;
	private final SlidePathResolver pathResolver;
	private final SlideRenderer renderer;

	private final LockMap<UUID> locks;
	private final Object exportLock;
	
	private final Map<KnownFormat, DataFormatProvider<Slide>> formatProviders;
	
	public SlidePersistAdapter(Path basePath, SlideRenderer renderer, SlideConfiguration configuration) {
		this.configuration = configuration;
		this.pathResolver = new SlidePathResolver(basePath, EXTENSION);
		this.renderer = renderer;
		
		this.locks = new LockMap<>();
		this.exportLock = new Object();
		
		this.formatProviders = new LinkedHashMap<>();
		this.formatProviders.put(KnownFormat.PRAISENTER3, new PraisenterFormatProvider<>(Slide.class));
	}
	
	@Override
	public void initialize() throws IOException {
		this.pathResolver.initialize();
	}
	
	@Override
	public List<Slide> load() throws IOException {
		List<Slide> items = new ArrayList<Slide>();
		try (DirectoryStream<Path> stream = Files.newDirectoryStream(this.pathResolver.getBasePath())) {
			for (Path file : stream) {
				if (Files.isRegularFile(file)) {
					String mimeType = MimeType.get(file);
					if (MimeType.JSON.is(mimeType)) {
						try (InputStream is = Files.newInputStream(file)) {
							Slide s = JsonIO.read(is, Slide.class);
							s.setThumbnailPath(this.pathResolver.getThumbPath(s));
							items.add(s);
						} catch (Exception ex) {
							LOGGER.error("Failed to load '" + file.toAbsolutePath() + "' due to: " + ex.getMessage(), ex);
						}
					}
				}
			}
		}
		return items;
	}
	
	@Override
	public void create(Slide item) throws IOException {
		Path path = this.pathResolver.getPath(item);
		synchronized (this.locks.get(item.getId())) {
			if (Files.exists(path)) {
				throw new FileAlreadyExistsException(path.toAbsolutePath().toString());
			}
			Path thumbnailPath = this.pathResolver.getThumbPath(item);
			BufferedImage image = this.renderer.renderThumbnail(item, this.configuration.getThumbnailWidth(), this.configuration.getThumbnailHeight());
			ImageIO.write(image, this.pathResolver.getThumbExtension(), thumbnailPath.toFile());
			JsonIO.write(path, item);
			item.setThumbnailPath(thumbnailPath);
		}
	}
	
	@Override
	public void update(Slide item) throws IOException {
		synchronized (this.exportLock) {
			Path path = this.pathResolver.getPath(item);
			synchronized (this.locks.get(item.getId())) {
				BufferedImage image = this.renderer.renderThumbnail(item, this.configuration.getThumbnailWidth(), this.configuration.getThumbnailHeight());
				ImageIO.write(image, this.pathResolver.getThumbExtension(), this.pathResolver.getThumbPath(item).toFile());
				JsonIO.write(path, item);
			}
		}
	}
	
	@Override
	public void delete(Slide item) throws IOException {
		synchronized (this.exportLock) {
			Path path = this.pathResolver.getPath(item);
			synchronized (this.locks.get(item.getId())) {
				Files.deleteIfExists(path);
				Files.deleteIfExists(this.pathResolver.getThumbPath(item));<commit_msg>Fixed issue of thumbnail path not being set before save on create<commit_after>package org.praisenter.data.slide;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;

import org.apache.commons.io.FilenameUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.praisenter.LockMap;
import org.praisenter.data.DataFormatProvider;
import org.praisenter.data.DataImportResult;
import org.praisenter.data.DataReadResult;
import org.praisenter.data.KnownFormat;
import org.praisenter.data.PersistAdapter;
import org.praisenter.data.PraisenterFormatProvider;
import org.praisenter.data.UnknownFormatException;
import org.praisenter.data.json.JsonIO;
import org.praisenter.utility.MimeType;
import org.praisenter.utility.Streams;

public final class SlidePersistAdapter implements PersistAdapter<Slide> {
	private static final Logger LOGGER = LogManager.getLogger();
	private static final String EXTENSION = "json";
	
	private final SlideConfiguration configuration;
	private final SlidePathResolver pathResolver;
	private final SlideRenderer renderer;

	private final LockMap<UUID> locks;
	private final Object exportLock;
	
	private final Map<KnownFormat, DataFormatProvider<Slide>> formatProviders;
	
	public SlidePersistAdapter(Path basePath, SlideRenderer renderer, SlideConfiguration configuration) {
		this.configuration = configuration;
		this.pathResolver = new SlidePathResolver(basePath, EXTENSION);
		this.renderer = renderer;
		
		this.locks = new LockMap<>();
		this.exportLock = new Object();
		
		this.formatProviders = new LinkedHashMap<>();
		this.formatProviders.put(KnownFormat.PRAISENTER3, new PraisenterFormatProvider<>(Slide.class));
	}
	
	@Override
	public void initialize() throws IOException {
		this.pathResolver.initialize();
	}
	
	@Override
	public List<Slide> load() throws IOException {
		List<Slide> items = new ArrayList<Slide>();
		try (DirectoryStream<Path> stream = Files.newDirectoryStream(this.pathResolver.getBasePath())) {
			for (Path file : stream) {
				if (Files.isRegularFile(file)) {
					String mimeType = MimeType.get(file);
					if (MimeType.JSON.is(mimeType)) {
						try (InputStream is = Files.newInputStream(file)) {
							Slide s = JsonIO.read(is, Slide.class);
							s.setThumbnailPath(this.pathResolver.getThumbPath(s));
							items.add(s);
						} catch (Exception ex) {
							LOGGER.error("Failed to load '" + file.toAbsolutePath() + "' due to: " + ex.getMessage(), ex);
						}
					}
				}
			}
		}
		return items;
	}
	
	@Override
	public void create(Slide item) throws IOException {
		Path path = this.pathResolver.getPath(item);
		synchronized (this.locks.get(item.getId())) {
			if (Files.exists(path)) {
				throw new FileAlreadyExistsException(path.toAbsolutePath().toString());
			}
			Path thumbnailPath = this.pathResolver.getThumbPath(item);
			BufferedImage image = this.renderer.renderThumbnail(item, this.configuration.getThumbnailWidth(), this.configuration.getThumbnailHeight());
			ImageIO.write(image, this.pathResolver.getThumbExtension(), thumbnailPath.toFile());
			item.setThumbnailPath(thumbnailPath);
			JsonIO.write(path, item);
		}
	}
	
	@Override
	public void update(Slide item) throws IOException {
		synchronized (this.exportLock) {
			Path path = this.pathResolver.getPath(item);
			synchronized (this.locks.get(item.getId())) {
				BufferedImage image = this.renderer.renderThumbnail(item, this.configuration.getThumbnailWidth(), this.configuration.getThumbnailHeight());
				ImageIO.write(image, this.pathResolver.getThumbExtension(), this.pathResolver.getThumbPath(item).toFile());
				JsonIO.write(path, item);
			}
		}
	}
	
	@Override
	public void delete(Slide item) throws IOException {
		synchronized (this.exportLock) {
			Path path = this.pathResolver.getPath(item);
			synchronized (this.locks.get(item.getId())) {
				Files.deleteIfExists(path);
				Files.deleteIfExists(this.pathResolver.getThumbPath(item));
{'fraction_non_alphanumeric': 0.11232517482517483, 'fraction_numerical': 0.0006555944055944056, 'mean_word_length': 5.201219512195122, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4332612', 'n_tokens_mistral': 3085, 'n_tokens_neox': 2796, 'n_words': 550}