package io.andrewohara.tinkertime.models.mod; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Path; import java.sql.SQLException; import java.util.Collection; import java.util.Date; import java.util.LinkedList; import javax.imageio.ImageIO; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.field.DataType; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.field.ForeignCollectionField; import com.j256.ormlite.misc.BaseDaoEnabled; import com.j256.ormlite.table.DatabaseTable; import io.andrewohara.common.version.Version; import io.andrewohara.tinkertime.TinkerTimeLauncher; import io.andrewohara.tinkertime.models.Installation; import io.andrewohara.tinkertime.models.ModFile; /** * Model for holding Mod information and status. * * Two flags can be set: enabled, and update available. * The Application will display this mod differently depending on the state * of these flags. * * @author Andrew O'Hara */ @DatabaseTable(tableName = "mods") public class Mod extends BaseDaoEnabled<Mod, Integer> implements Comparable<Mod> { public static final Dimension MAX_IMAGE_SIZE = new Dimension(250, 250); @DatabaseField(generatedId=true) private int id; @DatabaseField private Date updatedOn; @DatabaseField private String name, creator, modVersion, kspVersion; @DatabaseField(canBeNull = false) private String url; @DatabaseField private boolean updateAvailable = false, builtIn = false; @DatabaseField(foreign = true, canBeNull = false) private Installation installation; @ForeignCollectionField private Collection<ModFile> modFiles = new LinkedList<>(); private Collection<ModFile> cachedModFiles; @DatabaseField(dataType = DataType.BYTE_ARRAY) private byte[] imageBytes; @DatabaseField(dataType = DataType.LONG_STRING) private String readmeText = ""; // Used by ormlite Mod() { } public Mod(URL url, Installation installation, Dao<Mod, Integer> dao) throws SQLException{ this(url, installation, false, dao); } public Mod(URL url, Installation installation, boolean builtIn, Dao<Mod, Integer> dao) throws SQLException{ this.url = url != null ? url.toString() : null; this.installation = installation; if (installation != null) installation.addMod(this); this.builtIn = builtIn; setDao(dao); } // Used only for updating the TinkerTime App public static Mod newModManagerMod(){ Mod mod = new Mod(); mod.url = TinkerTimeLauncher.DOWNLOAD_URL; mod.modVersion = TinkerTimeLauncher.VERSION.getNormalVersion(); return mod; } ///////////// // Setters // ///////////// public void update(ModUpdateData updateData) throws SQLException{ this.name = updateData.name; this.creator = updateData.creator; this.updatedOn = updateData.updatedOn; this.kspVersion = updateData.kspVersion; this.modVersion = updateData.modVersion != null ? updateData.modVersion.getNormalVersion() : null; } public void setUpdateAvailable(boolean updateAvailable) throws SQLException{ this.updateAvailable = updateAvailable; } public void setModFiles(Collection<ModFile> modFiles) throws SQLException{ this.modFiles = modFiles; this.cachedModFiles = modFiles; } public void setImage(BufferedImage image) throws IOException, SQLException{ if (image != null){ ByteArrayOutputStream baos=new ByteArrayOutputStream(); ImageIO.write(image, "png", baos); this.imageBytes = baos.toByteArray(); } else { this.imageBytes = null; } } public void setReadmeText(String text) throws SQLException { this.readmeText = text; } ///////////// // Getters // ///////////// public int getId(){ return id; } public String getName(){ if (name == null){ return (url != null ? getUrl().getHost(): "Local") + " Mod"; } return name; } public String getCreator(){ return creator; } public URL getUrl(){ try { return new URL(url); } catch (MalformedURLException e) { return null; } } public Date getUpdatedOn(){ return updatedOn; } public Version getModVersion(){ return modVersion != null ? Version.valueOf(modVersion) : null; } /** * Returns the version of KSP that this mod version supports. * @return supported KSP version */ public String getSupportedVersion(){ return kspVersion; } public boolean isUpdateable(){ return url != null; } public boolean isUpdateAvailable(){ return updateAvailable; } public Installation getInstallation(){ return installation; } public Path getZipPath() { return installation.getModZipsPath().resolve(getId() + ".zip"); } public boolean isDownloaded(){ return getZipPath().toFile().exists(); } public boolean isEnabled(){ if (getModFiles().isEmpty()) return false; for (ModFile modFile : getModFiles()){ if (!modFile.getDestPath().toFile().exists()){ return false; } } return true; } public Collection<ModFile> getModFiles(){ if (cachedModFiles == null){ cachedModFiles = new LinkedList<>(modFiles); } return cachedModFiles; } public BufferedImage getImage(){ try { return ImageIO.read(new ByteArrayInputStream(imageBytes)); } catch (IOException | NullPointerException e) { return null; } } public String getReadmeText(){ return readmeText; } public boolean isBuiltIn(){ return builtIn; } ///////// // Dao // ///////// public int commit() throws SQLException{ if (dao != null){ return getId() == 0 ? create() : update(); } System.err.println("No Dao configured to update " + getName()); return 0; } @Override public int delete() throws SQLException { if (installation != null){ installation.removeMod(this); } return super.delete(); } //////////////// // Comparable // //////////////// @Override public int compareTo(Mod other) { return Integer.compare(getId(), other.getId()); } //////////// // Object // //////////// @Override public String toString(){ return getName(); } @Override public int hashCode(){ return new Integer(id).hashCode(); } @Override public boolean equals(Object o){ if (o instanceof Mod){ Mod mod = (Mod) o; return getId() == mod.getId() || getName().equals(mod.getName()); } return false; } }