/* * jSite - Project.java - Copyright © 2006–2014 David Roden * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. */ package de.todesbaum.jsite.application; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import net.pterodactylus.util.io.MimeTypes; /** * Container for project information. * * @author David ‘Bombe’ Roden <bombe@freenetproject.org> */ public class Project implements Comparable<Project> { /** The name of the project. */ protected String name; /** The description of the project. */ protected String description; /** The insert URI of the project. */ protected String insertURI; /** The request URI of the project. */ protected String requestURI; /** The index file of the project. */ protected String indexFile; /** The local path of the project. */ protected String localPath; /** The remote path of the URI. */ protected String path; /** The time of the last insertion. */ protected long lastInsertionTime; /** The edition to insert to. */ protected int edition; /** Whether to always force inserts. */ private boolean alwaysForceInserts; /** Whether to ignore hidden directory. */ private boolean ignoreHiddenFiles; /** Options for files. */ protected Map<String, FileOption> fileOptions = new HashMap<String, FileOption>(); /** * Empty constructor. */ public Project() { /* do nothing. */ } /** * Creates a new project from an existing one. * * @param project * The project to clone */ public Project(Project project) { name = project.name; description = project.description; insertURI = project.insertURI; requestURI = project.requestURI; path = project.path; edition = project.edition; localPath = project.localPath; indexFile = project.indexFile; lastInsertionTime = project.lastInsertionTime; alwaysForceInserts = project.alwaysForceInserts; ignoreHiddenFiles = project.ignoreHiddenFiles; for (Entry<String, FileOption> fileOption : fileOptions.entrySet()) { fileOptions.put(fileOption.getKey(), new FileOption(fileOption.getValue())); } } /** * Returns the name of the project. * * @return The name of the project */ public String getName() { return name; } /** * Sets the name of the project. * * @param name * The name of the project */ public void setName(String name) { this.name = name; } /** * Returns the description of the project. * * @return The description of the project */ public String getDescription() { return description; } /** * Sets the description of the project. * * @param description * The description of the project */ public void setDescription(String description) { this.description = description; } /** * Returns the local path of the project. * * @return The local path of the project */ public String getLocalPath() { return localPath; } /** * Sets the local path of the project. * * @param localPath * The local path of the project */ public void setLocalPath(String localPath) { this.localPath = localPath; } /** * Returns the name of the index file of the project, relative to the * project’s local path. * * @return The name of the index file of the project */ public String getIndexFile() { return indexFile; } /** * Sets the name of the index file of the project, relative to the project’s * local path. * * @param indexFile * The name of the index file of the project */ public void setIndexFile(String indexFile) { this.indexFile = indexFile; } /** * Returns the time the project was last inserted, in milliseconds since the * epoch. * * @return The time of the last insertion */ public long getLastInsertionTime() { return lastInsertionTime; } /** * Sets the time the project was last inserted, in milliseconds since the * last epoch. * * @param lastInserted * The time of the last insertion */ public void setLastInsertionTime(long lastInserted) { lastInsertionTime = lastInserted; } /** * Returns the remote path of the project. The remote path is the path that * directly follows the request URI of the project. * * @return The remote path of the project */ public String getPath() { return path; } /** * Sets the remote path of the project. The remote path is the path that * directly follows the request URI of the project. * * @param path * The remote path of the project */ public void setPath(String path) { this.path = path; } /** * Returns the insert URI of the project. * * @return The insert URI of the project */ public String getInsertURI() { return insertURI; } /** * Sets the insert URI of the project. * * @param insertURI * The insert URI of the project */ public void setInsertURI(String insertURI) { this.insertURI = shortenURI(insertURI); } /** * Returns the request URI of the project. * * @return The request URI of the project */ public String getRequestURI() { return requestURI; } /** * Sets the request URI of the project. * * @param requestURI * The request URI of the project */ public void setRequestURI(String requestURI) { this.requestURI = shortenURI(requestURI); } /** * Returns whether files for this project should always be inserted, even * when unchanged. * * @return {@code true} to always force inserts on this project, * {@code false} otherwise */ public boolean isAlwaysForceInsert() { return alwaysForceInserts; } /** * Sets whether files for this project should always be inserted, even when * unchanged. * * @param alwaysForceInsert * {@code true} to always force inserts on this project, * {@code false} otherwise */ public void setAlwaysForceInsert(boolean alwaysForceInsert) { this.alwaysForceInserts = alwaysForceInsert; } /** * Returns whether hidden files are ignored, i.e. not inserted. * * @return {@code true} if hidden files are not inserted, {@code false} * otherwise */ public boolean isIgnoreHiddenFiles() { return ignoreHiddenFiles; } /** * Sets whether hidden files are ignored, i.e. not inserted. * * @param ignoreHiddenFiles * {@code true} if hidden files are not inserted, {@code false} * otherwise */ public void setIgnoreHiddenFiles(boolean ignoreHiddenFiles) { this.ignoreHiddenFiles = ignoreHiddenFiles; } /** * {@inheritDoc} * <p> * This method returns the name of the project. */ @Override public String toString() { return name; } /** * Shortens the given URI by removing scheme and key-type prefixes. * * @param uri * The URI to shorten * @return The shortened URI */ private static String shortenURI(String uri) { String shortUri = uri; if (shortUri.startsWith("freenet:")) { shortUri = shortUri.substring("freenet:".length()); } if (shortUri.startsWith("SSK@")) { shortUri = shortUri.substring("SSK@".length()); } if (shortUri.startsWith("USK@")) { shortUri = shortUri.substring("USK@".length()); } if (shortUri.endsWith("/")) { shortUri = shortUri.substring(0, shortUri.length() - 1); } return shortUri; } /** * Shortens the name of the given file by removing the local path of the * project and leading file separators. * * @param file * The file whose name should be shortened * @return The shortened name of the file */ public String shortenFilename(File file) { String filename = file.getPath(); if (filename.startsWith(localPath)) { filename = filename.substring(localPath.length()); if (filename.startsWith(File.separator)) { filename = filename.substring(1); } } return filename; } /** * Returns the options for the file with the given name. If the file does * not yet have any options, a new set of default options is created and * returned. * * @param filename * The name of the file, relative to the project root * @return The options for the file */ public FileOption getFileOption(String filename) { FileOption fileOption = fileOptions.get(filename); String defaultMimeType = "application/octet-stream"; if (fileOption == null) { List<String> suffixes = getSuffixes(filename); for (String suffix : suffixes) { String mimeType = MimeTypes.getMimeType(suffix); if (!mimeType.equals(defaultMimeType)) { defaultMimeType = mimeType; break; } } fileOption = new FileOption(defaultMimeType); } fileOptions.put(filename, fileOption); return fileOption; } private List<String> getSuffixes(String filename) { List<String> suffixes = new ArrayList<>(); int dot = filename.lastIndexOf("."); while (dot > -1) { String suffix = filename.substring(dot + 1); suffixes.add(0, suffix); dot = filename.lastIndexOf(".", dot - 1); } return suffixes; } /** * Sets options for a file. * * @param filename * The filename to set the options for, relative to the project * root * @param fileOption * The options to set for the file, or <code>null</code> to * remove the options for the file */ public void setFileOption(String filename, FileOption fileOption) { if (fileOption != null) { fileOptions.put(filename, fileOption); } else { fileOptions.remove(filename); } } /** * Returns all file options. * * @return All file options */ public Map<String, FileOption> getFileOptions() { return Collections.unmodifiableMap(fileOptions); } /** * Sets all file options. * * @param fileOptions * The file options */ public void setFileOptions(Map<String, FileOption> fileOptions) { this.fileOptions.clear(); this.fileOptions.putAll(fileOptions); } /** * {@inheritDoc} * <p> * Projects are compared by their name only. */ @Override public int compareTo(Project project) { return name.compareToIgnoreCase(project.name); } /** * Returns the edition of the project. * * @return The edition of the project */ public int getEdition() { return edition; } /** * Sets the edition of the project. * * @param edition * The edition to set */ public void setEdition(int edition) { this.edition = edition; } /** * Constructs the final request URI including the edition number. * * @param offset * The offset for the edition number * @return The final request URI */ public String getFinalRequestURI(int offset) { return "USK@" + requestURI + "/" + path + "/" + (edition + offset) + "/"; } /** * Performs some post-processing on the project after it was inserted * successfully. At the moment it copies the current hashes of all file * options to the last insert hashes, updating the hashes for the next * insert. */ public void onSuccessfulInsert() { for (Entry<String, FileOption> fileOptionEntry : fileOptions.entrySet()) { FileOption fileOption = fileOptionEntry.getValue(); if ((fileOption.getCurrentHash() != null) && (fileOption.getCurrentHash().length() > 0) && (!fileOption.getCurrentHash().equals(fileOption.getLastInsertHash()) || fileOption.isForceInsert())) { fileOption.setLastInsertEdition(edition); fileOption.setLastInsertHash(fileOption.getCurrentHash()); fileOption.setLastInsertFilename(fileOption.getChangedName().orElse(fileOptionEntry.getKey())); } fileOption.setForceInsert(false); } } }