Java tutorial
/* * (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org> * * This file is part of the Kitodo project. * * It is licensed under GNU General Public License version 3 or later. * * For the full copyright and license information, please read the * GPL3-License.txt file that was distributed with this source code. */ package org.kitodo.production.services.file; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileSystems; import java.nio.file.Paths; import java.text.MessageFormat; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.kitodo.api.command.CommandResult; import org.kitodo.api.dataformat.MediaUnit; import org.kitodo.api.dataformat.MediaVariant; import org.kitodo.api.dataformat.Workpiece; import org.kitodo.api.filemanagement.FileManagementInterface; import org.kitodo.api.filemanagement.ProcessSubType; import org.kitodo.config.ConfigCore; import org.kitodo.config.enums.ParameterCore; import org.kitodo.data.database.beans.Folder; import org.kitodo.data.database.beans.Process; import org.kitodo.data.database.beans.Ruleset; import org.kitodo.data.database.beans.User; import org.kitodo.data.database.enums.MetadataFormat; import org.kitodo.data.database.exceptions.DAOException; import org.kitodo.production.file.BackupFileRotation; import org.kitodo.production.helper.Helper; import org.kitodo.production.helper.metadata.ImageHelper; import org.kitodo.production.helper.metadata.legacytypeimplementations.LegacyMetsModsDigitalDocumentHelper; import org.kitodo.production.helper.metadata.pagination.Paginator; import org.kitodo.production.metadata.comparator.MetadataImageComparator; import org.kitodo.production.model.Subfolder; import org.kitodo.production.services.ServiceManager; import org.kitodo.production.services.command.CommandService; import org.kitodo.production.services.data.RulesetService; import org.kitodo.production.services.data.UserService; import org.kitodo.serviceloader.KitodoServiceLoader; public class FileService { private static final String SYSTEM_LOCKING_USER = "System"; private static final Logger logger = LogManager.getLogger(FileService.class); private static final String TEMPORARY_FILENAME_PREFIX = "temporary_"; private volatile FileManagementInterface fileManagementModule = new KitodoServiceLoader<FileManagementInterface>( FileManagementInterface.class).loadModule(); /** * Creates a MetaDirectory. * * @param parentFolderUri * The URI, where the * @param directoryName * the name of the directory * @return true or false * @throws IOException * an IOException */ URI createMetaDirectory(URI parentFolderUri, String directoryName) throws IOException { if (!fileExist(parentFolderUri.resolve(directoryName))) { CommandService commandService = ServiceManager.getCommandService(); String path = FileSystems.getDefault() .getPath(ConfigCore.getKitodoDataDirectory(), parentFolderUri.getRawPath(), directoryName) .normalize().toAbsolutePath().toString(); List<String> commandParameter = Collections.singletonList(path); File script = new File(ConfigCore.getParameter(ParameterCore.SCRIPT_CREATE_DIR_META)); CommandResult commandResult = commandService.runCommand(script, commandParameter); if (!commandResult.isSuccessful()) { String message = MessageFormat.format( "Could not create directory {0} in {1}! No new directory was created", directoryName, parentFolderUri.getPath()); logger.warn(message); throw new IOException(message); } } else { logger.info("Metadata directory: {} already existed! No new directory was created", directoryName); } return URI.create(parentFolderUri.getPath() + '/' + directoryName); } /** * Creates a directory including any missing parent directories. * * @param pathToCreate * path to create */ public void createDirectories(URI pathToCreate) throws IOException { if (fileManagementModule.isDirectory(pathToCreate)) { return; } String path = pathToCreate.toString(); int lastSlash = path.lastIndexOf('/'); if (lastSlash <= 0) { createDirectory(null, path); } else { URI before = URI.create(path.substring(0, lastSlash)); String after = path.substring(lastSlash + 1); createDirectories(before); createDirectory(before, after); } } /** * Creates a directory at a given URI with a given name. * * @param parentFolderUri * the uri, where the directory should be created * @param directoryName * the name of the directory. * @return the URI of the new directory or URI of parent directory if * directoryName is null or empty */ public URI createDirectory(URI parentFolderUri, String directoryName) throws IOException { if (Objects.nonNull(directoryName)) { return fileManagementModule.create(parentFolderUri, directoryName, false); } return URI.create(""); } /** * Creates a directory with a name given and assigns permissions to the * given user. Under Linux a script is used to set the file system * permissions accordingly. This cannot be done from within java code before * version 1.7. * * @param dirName * Name of directory to create * @throws IOException * If an I/O error occurs. */ public void createDirectoryForUser(URI dirName, String userName) throws IOException { if (!fileExist(dirName)) { CommandService commandService = ServiceManager.getCommandService(); List<String> commandParameter = Arrays.asList(userName, new File(dirName).getAbsolutePath()); commandService.runCommand(new File(ConfigCore.getParameter(ParameterCore.SCRIPT_CREATE_DIR_USER_HOME)), commandParameter); } } /** * Creates the folder structure needed for a process. * * @param process * the process * @return the URI to the process location */ public URI createProcessLocation(Process process) throws IOException { URI processLocationUri = fileManagementModule.createProcessLocation(process.getId().toString()); for (Folder folder : process.getProject().getFolders()) { if (folder.isCreateFolder()) { URI parentFolderUri = processLocationUri; for (String singleFolder : new Subfolder(process, folder).getRelativeDirectoryPath() .split(Pattern.quote(File.separator))) { parentFolderUri = createMetaDirectory(parentFolderUri, singleFolder); } } } return processLocationUri; } /** * Creates a new File. * * @param fileName * the name of the new file * @return the uri of the new file */ public URI createResource(String fileName) throws IOException { return fileManagementModule.create(null, fileName, true); } /** * Creates a resource at a given URI with a given name. * * @param targetFolder * the URI of the target folder * @param name * the name of the new resource * @return the URI of the created resource */ public URI createResource(URI targetFolder, String name) throws IOException { return fileManagementModule.create(targetFolder, name, true); } /** * Writes to a file at a given URI. * * @param uri * the URI, to write to. * @return an output stream to the file at the given URI or null */ public OutputStream write(URI uri) throws IOException { return fileManagementModule.write(uri); } /** * Gets and returns the name of the user whose context the code is currently * running in, to request or assume meta-data locks for that user. The name * of the user is returned, or System?, if the code is running in the * system context (i.e. not running under a registered user). * * @return the user name for locks */ public static String getCurrentLockingUser() { UserService userService = ServiceManager.getUserService(); User currentUser = userService.getAuthenticatedUser(); return Objects.nonNull(currentUser) ? userService.getFullName(currentUser) : SYSTEM_LOCKING_USER; } /** * Reads a file at a given URI. * * @param uri * the uri to read * @return an InputStream to read from or null */ public InputStream read(URI uri) throws IOException { return fileManagementModule.read(uri); } /** * Read metadata file (meta.xml). * * @param process * for which file should be read * @return InputStream with metadata file */ public InputStream readMetadataFile(Process process) throws IOException { return read(getMetadataFilePath(process)); } /** * This function implements file renaming. Renaming of files is full of * mischief under Windows which unaccountably holds locks on files. * Sometimes running the JVMs garbage collector puts things right. * * @param fileUri * File to rename * @param newFileName * New file name / destination * @throws IOException * is thrown if the rename fails permanently */ public URI renameFile(URI fileUri, String newFileName) throws IOException { return fileManagementModule.rename(fileUri, newFileName); } /** * Calculate all files with given file extension at specified directory * recursively. * * @param directory * the directory to run through * @return number of files as Integer */ public Integer getNumberOfFiles(URI directory) { return fileManagementModule.getNumberOfFiles(null, directory); } /** * Calculate all files with given file extension at specified directory * recursively. * * @param directory * the directory to run through * @return number of files as Integer */ public Integer getNumberOfImageFiles(URI directory) { return fileManagementModule.getNumberOfFiles(ImageHelper.imageNameFilter, directory); } /** * Get size of directory. * * @param directory * URI to get size * @return size of directory as Long */ public Long getSizeOfDirectory(URI directory) throws IOException { return fileManagementModule.getSizeOfDirectory(directory); } /** * Copy directory. * * @param sourceDirectory * source file as uri * @param targetDirectory * destination file as uri */ public void copyDirectory(URI sourceDirectory, URI targetDirectory) throws IOException { fileManagementModule.copy(sourceDirectory, targetDirectory); } /** * Copies a file from a given URI to a given URI. * * @param sourceUri * the uri to copy from * @param destinationUri * the uri to copy to * @throws IOException * if copying fails */ public void copyFile(URI sourceUri, URI destinationUri) throws IOException { fileManagementModule.copy(sourceUri, destinationUri); } /** * Copies a file to a directory. * * @param sourceDirectory * The source directory * @param targetDirectory * the target directory * @throws IOException * if copying fails. */ public void copyFileToDirectory(URI sourceDirectory, URI targetDirectory) throws IOException { String target = targetDirectory.toString(); if (!target.endsWith("/")) { targetDirectory = URI.create(target.concat("/")); } fileManagementModule.copy(sourceDirectory, targetDirectory); } /** * Deletes a resource at a given URI. * * @param uri * the uri to delete * @return true, if successful, false otherwise * @throws IOException * if get of module fails */ public boolean delete(URI uri) throws IOException { return fileManagementModule.delete(uri); } /** * Checks, if a file exists. * * @param uri * the URI, to check, if there is a file * @return true, if the file exists */ public boolean fileExist(URI uri) { return fileManagementModule.fileExist(uri); } /** * Checks if a resource at a given URI is a file. * * @param uri * the URI to check, if there is a file * @return true, if it is a file, false otherwise */ public boolean isFile(URI uri) { return fileManagementModule.isFile(uri); } /** * checks, if a URI leads to a directory. * * @param dir * the uri to check. * @return true, if it is a directory. */ public boolean isDirectory(URI dir) { return fileManagementModule.isDirectory(dir); } /** * Checks if an uri is readable. * * @param uri * the uri to check. * @return true, if it's readable, false otherwise. */ public boolean canRead(URI uri) { return fileManagementModule.canRead(uri); } /** * Returns the name of a file at a given URI. * * @param uri * the URI, to get the filename from. * @return the name of the file */ public String getFileName(URI uri) { String fileNameWithExtension = fileManagementModule.getFileNameWithExtension(uri); if (fileNameWithExtension.contains(".")) { return fileNameWithExtension.substring(0, fileNameWithExtension.indexOf('.')); } return fileNameWithExtension; } /** * Returns the name of a file at a given uri. * * @param uri * the URI, to get the filename from * @return the name of the file */ public String getFileNameWithExtension(URI uri) { return fileManagementModule.getFileNameWithExtension(uri); } /** * Moves a directory from a given URI to a given URI. * * @param sourceUri * the source URI * @param targetUri * the target URI * @throws IOException * if get of module fails */ public void moveDirectory(URI sourceUri, URI targetUri) throws IOException { fileManagementModule.move(sourceUri, targetUri); } /** * Moves a file from a given URI to a given URI. * * @param sourceUri * the source URI * @param targetUri * the target URI * @throws IOException * if get of module fails */ public void moveFile(URI sourceUri, URI targetUri) throws IOException { fileManagementModule.move(sourceUri, targetUri); } /** * Get all sub URIs of an URI. * * @param uri * the URI, to get the sub URIs from * @return a List of sub URIs */ public List<URI> getSubUris(URI uri) { return fileManagementModule.getSubUris(null, uri); } /** * Get all sub URIs of an URI with a given filter. * * @param filter * the filter to filter the sub URIs * @param uri * the URI, to get the sub URIs from * @return a List of sub URIs */ public List<URI> getSubUris(FilenameFilter filter, URI uri) { return fileManagementModule.getSubUris(filter, uri); } /** * Lists all Files at the given Path. * * @param file * the directory to get the Files from * @return an Array of Files. */ private File[] listFiles(File file) { File[] unchecked = file.listFiles(); return Objects.nonNull(unchecked) ? unchecked : new File[0]; } /** * Writes a metadata file. * * @param gdzfile * the file format * @param process * the process * @throws IOException * if error occurs */ public void writeMetadataFile(LegacyMetsModsDigitalDocumentHelper gdzfile, Process process) throws IOException { RulesetService rulesetService = ServiceManager.getRulesetService(); LegacyMetsModsDigitalDocumentHelper ff; Ruleset ruleset = process.getRuleset(); switch (MetadataFormat.findFileFormatsHelperByName(process.getProject().getFileFormatInternal())) { case METS: ff = new LegacyMetsModsDigitalDocumentHelper(rulesetService.getPreferences(ruleset).getRuleset()); break; default: throw new UnsupportedOperationException("Dead code pending removal"); } // createBackupFile(); URI metadataFileUri = getMetadataFilePath(process, false); String temporaryMetadataFileName = getTemporaryMetadataFileName(metadataFileUri); ff.setDigitalDocument(gdzfile.getDigitalDocument()); // ff.write(getMetadataFilePath()); ff.write(temporaryMetadataFileName); File temporaryMetadataFile = new File(temporaryMetadataFileName); boolean backupCondition = temporaryMetadataFile.exists() && temporaryMetadataFile.length() > 0; if (backupCondition) { createBackupFile(process); renameFile(Paths.get(temporaryMetadataFileName).toUri(), metadataFileUri.getRawPath()); removePrefixFromRelatedMetsAnchorFilesFor(Paths.get(temporaryMetadataFileName).toUri()); } } private void removePrefixFromRelatedMetsAnchorFilesFor(URI temporaryMetadataFilename) throws IOException { File temporaryFile = new File(temporaryMetadataFilename); File directoryPath = new File(temporaryFile.getParentFile().getPath()); for (File temporaryAnchorFile : listFiles(directoryPath)) { String temporaryAnchorFileName = temporaryAnchorFile.toString(); if (temporaryAnchorFile.isFile() && FilenameUtils.getBaseName(temporaryAnchorFileName).startsWith(TEMPORARY_FILENAME_PREFIX)) { String anchorFileName = FilenameUtils.concat(FilenameUtils.getFullPath(temporaryAnchorFileName), temporaryAnchorFileName.replace(TEMPORARY_FILENAME_PREFIX, "")); temporaryAnchorFileName = FilenameUtils.concat(FilenameUtils.getFullPath(temporaryAnchorFileName), temporaryAnchorFileName); renameFile(Paths.get(temporaryAnchorFileName).toUri(), new File(anchorFileName).toURI().getRawPath()); } } } // backup of meta.xml void createBackupFile(Process process) throws IOException { int numberOfBackups; numberOfBackups = ConfigCore.getIntParameter(ParameterCore.NUMBER_OF_META_BACKUPS); if (numberOfBackups != ConfigCore.INT_PARAMETER_NOT_DEFINED_OR_ERRONEOUS) { BackupFileRotation bfr = new BackupFileRotation(); bfr.setNumberOfBackups(numberOfBackups); bfr.setFormat("meta.*\\.xml"); bfr.setProcess(process); bfr.performBackup(); } else { logger.warn("No backup configured for meta data files."); } } /** * Gets the URI of the metadata.xml of a given process. * * @param process * the process to get the metadata.xml for. * @return The URI to the metadata.xml */ public URI getMetadataFilePath(Process process) throws IOException { return getMetadataFilePath(process, true); } /** * Gets the URI of the metadata.xml of a given process. * * @param process * the process to get the metadata.xml for. * @param mustExist * whether the file must exist * @return The URI to the metadata.xml */ public URI getMetadataFilePath(Process process, boolean mustExist) throws IOException { URI metadataFilePath = getProcessSubTypeURI(process, ProcessSubType.META_XML, null); if (mustExist && !fileExist(metadataFilePath)) { throw new IOException(Helper.getTranslation("metadataFileNotFound", Collections.singletonList(metadataFilePath.getPath()))); } return metadataFilePath; } private String getTemporaryMetadataFileName(URI fileName) { File temporaryFile = getFile(fileName); String directoryPath = temporaryFile.getParentFile().getPath(); String temporaryFileName = TEMPORARY_FILENAME_PREFIX + temporaryFile.getName(); return directoryPath + File.separator + temporaryFileName; } /** * Gets the specific IMAGE sub type. * * @param process * the process to get the imageDirectory for. * @return The uri of the Image Directory. */ public URI getImagesDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.IMAGE, null); } /** * Gets the URI to the ocr directory. * * @param process * the process tog et the ocr directory for. * @return the uri to the ocr directory. */ public URI getOcrDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.OCR, null); } /** * Gets the URI to the import directory. * * @param process * the process to get the import directory for. * @return the uri of the import directory */ public URI getImportDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.IMPORT, null); } /** * Gets the URI to the text directory. * * @param process * the process to get the text directory for. * @return the uri of the text directory */ public URI getTxtDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.OCR_TXT, null); } /** * Gets the URI to the pdf directory. * * @param process * the process to get the pdf directory for. * @return the uri of the pdf directory */ public URI getPdfDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.OCR_PDF, null); } /** * Gets the URI to the alto directory. * * @param process * the process to get the alto directory for. * @return the uri of the alto directory */ public URI getAltoDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.OCR_ALTO, null); } /** * Gets the URI to the word directory. * * @param process * the process to get the word directory for. * @return the uri of the word directory */ public URI getWordDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.OCR_WORD, null); } /** * Gets the URI to the template file. * * @param process * the process to get the template file for. * @return the uri of the template file */ public URI getTemplateFile(Process process) { return getProcessSubTypeURI(process, ProcessSubType.TEMPLATE, null); } /** * This method is needed for migration purposes. It maps existing filePaths * to the correct URI. File.separator doesn't work because on Windows it * appends backslash to URI. * * @param process * the process, the uri is needed for. * @return the URI. */ public URI getProcessBaseUriForExistingProcess(Process process) { URI processBaseUri = process.getProcessBaseUri(); if (Objects.isNull(processBaseUri) && Objects.nonNull(process.getId())) { process.setProcessBaseUri(fileManagementModule.createUriForExistingProcess(process.getId().toString())); } return process.getProcessBaseUri(); } /** * Get the URI for a process sub-location. Possible locations are listed in * ProcessSubType. * * @param processId * the id of process to get the sublocation for * @param processTitle * the title of process to get the sublocation for * @param processDataDirectory * the base URI of process to get the sublocation for * @param processSubType * The subType. * @param resourceName * the name of the single object (e.g. image) if null, the root * folder of the sublocation is returned * @return The URI of the requested location */ public URI getProcessSubTypeURI(Integer processId, String processTitle, URI processDataDirectory, ProcessSubType processSubType, String resourceName) { if (Objects.isNull(processDataDirectory)) { try { Process process = ServiceManager.getProcessService().getById(processId); processDataDirectory = ServiceManager.getProcessService().getProcessDataDirectory(process); } catch (DAOException e) { processDataDirectory = URI.create(String.valueOf(processId)); } } if (Objects.isNull(resourceName)) { resourceName = ""; } return fileManagementModule.getProcessSubTypeUri(processDataDirectory, processTitle, processSubType, resourceName); } /** * Get's the URI for a Process Sub-location. Possible Locations are listed * in ProcessSubType * * @param process * the process to get the sublocation for. * @param processSubType * The subType. * @param resourceName * the name of the single object (e.g. image) if null, the root * folder of the sublocation is returned * @return The URI of the requested location */ public URI getProcessSubTypeURI(Process process, ProcessSubType processSubType, String resourceName) { URI processDataDirectory = ServiceManager.getProcessService().getProcessDataDirectory(process); if (Objects.isNull(resourceName)) { resourceName = ""; } return fileManagementModule.getProcessSubTypeUri(processDataDirectory, Helper.getNormalizedTitle(process.getTitle()), processSubType, resourceName); } /** * Get part of the URI for specific process. * * @param filter * FilenameFilter object * @param processId * the id of process * @param processTitle * the title of process * @param processDataDirectory * the base URI of process * @param processSubType * object * @param resourceName * as String * @return unmapped URI */ public List<URI> getSubUrisForProcess(FilenameFilter filter, Integer processId, String processTitle, URI processDataDirectory, ProcessSubType processSubType, String resourceName) { URI processSubTypeURI = getProcessSubTypeURI(processId, processTitle, processDataDirectory, processSubType, resourceName); return getSubUris(filter, processSubTypeURI); } /** * Get part of the URI for specific process. * * @param filter * FilenameFilter object * @param process * object * @param processSubType * object * @param resourceName * as String * @return unmapped URI */ public List<URI> getSubUrisForProcess(FilenameFilter filter, Process process, ProcessSubType processSubType, String resourceName) { URI processSubTypeURI = getProcessSubTypeURI(process, processSubType, resourceName); return getSubUris(filter, processSubTypeURI); } /** * Deletes all process directories and their content. * * @param process * the process to delete the directories for. * @return true, if deletion was successful. */ public boolean deleteProcessContent(Process process) throws IOException { for (ProcessSubType processSubType : ProcessSubType.values()) { URI processSubTypeURI = getProcessSubTypeURI(process, processSubType, null); if (!fileManagementModule.delete(processSubTypeURI)) { return false; } } return true; } /** * Gets the image source directory. * * @param process * the process, to get the source directory for * @return the source directory as an URI */ public URI getSourceDirectory(Process process) { return getProcessSubTypeURI(process, ProcessSubType.IMAGE_SOURCE, null); } /** * Gets the URI to the temporal directory. * * @return the URI to the temporal directory. */ public URI getTemporaryDirectory() { return ConfigCore.getUriParameter(ParameterCore.DIR_TEMP); } /** * Gets the URI to the users directory. * * @return the URI to the users directory. */ public URI getUsersDirectory() { return ConfigCore.getUriParameter(ParameterCore.DIR_USERS); } /** * Searches for new media and adds them to the media list of the workpiece, * if any are found. * * @param process * Process in which folders should be searched for media * @param workpiece * Workpiece to which the media are to be added */ public void searchForMedia(Process process, Workpiece workpiece) { final long begin = System.nanoTime(); List<Folder> folders = process.getProject().getFolders(); int mapCapacity = (int) Math.ceil(folders.size() / 0.75); Map<String, Subfolder> subfolders = new HashMap<>(mapCapacity); for (Folder folder : folders) { subfolders.put(folder.getFileGroup(), new Subfolder(process, folder)); } Map<String, Map<Subfolder, URI>> mediaToAdd = new TreeMap<>(new MetadataImageComparator()); for (Subfolder subfolder : subfolders.values()) { for (Entry<String, URI> element : subfolder.listContents().entrySet()) { mediaToAdd.computeIfAbsent(element.getKey(), any -> new HashMap<>(mapCapacity)); mediaToAdd.get(element.getKey()).put(subfolder, element.getValue()); } } List<String> canonicals = getCanonicalFileNamePartsAndSanitizeAbsoluteURIs(workpiece, subfolders, process.getProcessBaseUri()); addNewURIsToExistingMediaUnits(mediaToAdd, workpiece.getAllMediaUnits(), canonicals); for (String canonical : canonicals) { mediaToAdd.remove(canonical); } addNewMediaToWorkpiece(canonicals, mediaToAdd, workpiece); renumberMediaUnits(workpiece); if (ConfigCore.getBooleanParameter(ParameterCore.WITH_AUTOMATIC_PAGINATION)) { repaginateMediaUnits(workpiece); } if (logger.isTraceEnabled()) { logger.trace("Searching for media took {} ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - begin)); } } /** * Parses the canonical part of the filename from the URIs of the media * units. Because we need to do this to be able to parse correctly, old * absolute URIs are converted to relative URIs. */ private List<String> getCanonicalFileNamePartsAndSanitizeAbsoluteURIs(Workpiece workpiece, Map<String, Subfolder> subfolders, URI processBaseUri) { List<String> canonicals = new LinkedList<>(); String baseUriString = processBaseUri.toString(); if (!baseUriString.endsWith("/")) { baseUriString = baseUriString.concat("/"); } for (MediaUnit mediaUnit : workpiece.getAllMediaUnits()) { String unitCanonical = ""; for (Entry<MediaVariant, URI> entry : mediaUnit.getMediaFiles().entrySet()) { Subfolder subfolder = subfolders.get(entry.getKey().getUse()); if (Objects.isNull(subfolder)) { logger.warn("Missing subfolder for USE {}", entry.getKey().getUse()); continue; } URI mediaFile = entry.getValue(); String fileUriString = mediaFile.toString(); if (fileUriString.startsWith(baseUriString)) { mediaFile = URI.create(fileUriString.substring(baseUriString.length())); mediaUnit.getMediaFiles().put(entry.getKey(), mediaFile); } String fileCanonical = subfolder.getCanonical(mediaFile); if ("".equals(unitCanonical)) { unitCanonical = fileCanonical; } else if (unitCanonical.equals(fileCanonical)) { continue; } else { throw new IllegalArgumentException( "Ambiguous canonical file name part in the same media unit: \"" + unitCanonical + "\" and \"" + fileCanonical + "\"!"); } } if (mediaUnit.getMediaFiles().size() > 0 && "".equals(unitCanonical)) { throw new IllegalArgumentException("Missing canonical file name part in media unit " + mediaUnit); } canonicals.add(unitCanonical); } return canonicals; } /** * Adds new media variants found to existing media units. */ private void addNewURIsToExistingMediaUnits(Map<String, Map<Subfolder, URI>> mediaToAdd, List<MediaUnit> mediaUnits, List<String> canonicals) { for (int i = 0; i < canonicals.size(); i++) { String canonical = canonicals.get(i); MediaUnit mediaUnit = mediaUnits.get(i); if (mediaToAdd.containsKey(canonical)) { for (Entry<Subfolder, URI> entry : mediaToAdd.get(canonical).entrySet()) { mediaUnit.getMediaFiles().put(createMediaVariant(entry.getKey().getFolder()), entry.getValue()); } } } } /** * Adds the new media to the workpiece. The media are sorted in according to * the canonical part of the file name. */ private void addNewMediaToWorkpiece(List<String> canonicals, Map<String, Map<Subfolder, URI>> mediaToAdd, Workpiece workpiece) { for (Entry<String, Map<Subfolder, URI>> entry : mediaToAdd.entrySet()) { int insertionPoint = 0; for (String canonical : canonicals) { if (new MetadataImageComparator().compare(entry.getKey(), canonical) > 0) { insertionPoint++; } else { break; } } workpiece.getMediaUnits().add(insertionPoint, createMediaUnit(entry.getValue())); canonicals.add(insertionPoint, entry.getKey()); } } /** * Creates a new media unit with the given uses and URIs. */ private MediaUnit createMediaUnit(Map<Subfolder, URI> data) { MediaUnit result = new MediaUnit(); if (!data.entrySet().isEmpty()) { result.setType("page"); } for (Entry<Subfolder, URI> entry : data.entrySet()) { Folder folder = entry.getKey().getFolder(); MediaVariant mediaVariant = createMediaVariant(folder); result.getMediaFiles().put(mediaVariant, entry.getValue()); } return result; } /** * Creates a new media variant for the given use. */ private MediaVariant createMediaVariant(Folder folder) { MediaVariant mediaVariant = new MediaVariant(); mediaVariant.setUse(folder.getFileGroup()); mediaVariant.setMimeType(folder.getMimeType()); return mediaVariant; } /** * Renumbers the order of the media units. */ private void renumberMediaUnits(Workpiece workpiece) { int minimum = 1; for (MediaUnit mediaUnit : workpiece.getMediaUnits()) { if (mediaUnit.getOrder() > minimum) { minimum = mediaUnit.getOrder() + 1; } else { mediaUnit.setOrder(minimum); minimum++; } } } /** * Adds a count to media that do not yet have a count. But only at the end, * or if none of the media has been counted yet at all. New media found in * intermediate places are marked uncounted. */ private void repaginateMediaUnits(Workpiece workpiece) { List<MediaUnit> mediaUnits = workpiece.getMediaUnits(); int first = 0; String value; switch (ConfigCore.getParameter(ParameterCore.METS_EDITOR_DEFAULT_PAGINATION)) { case "arabic": value = "1"; break; case "roman": value = "I"; break; default: value = " - "; break; } for (int i = mediaUnits.size() - 1; i >= 0; i--) { MediaUnit mediaUnit = mediaUnits.get(i); String orderlabel = mediaUnit.getOrderlabel(); if (orderlabel == null || mediaUnit.getMediaFiles().isEmpty()) { continue; } first = i + 1; value = orderlabel; mediaUnits.get(i).setType("page"); break; } Paginator paginator = new Paginator(value); if (first > 0) { paginator.next(); for (int i = first; i < mediaUnits.size(); i++) { mediaUnits.get(i).setOrderlabel(paginator.next()); } } for (MediaUnit mediaUnit : mediaUnits) { if (Objects.isNull(mediaUnit.getOrderlabel())) { mediaUnit.setOrderlabel(" - "); } } } public void writeMetadataAsTemplateFile(LegacyMetsModsDigitalDocumentHelper inFile, Process process) throws IOException { inFile.write(getTemplateFile(process).toString()); } /** * Creates a symbolic link. * * @param targetUri * the target URI for the link * @param homeUri * the home URI * @return true, if link creation was successful */ public boolean createSymLink(URI homeUri, URI targetUri, boolean onlyRead, User user) { return fileManagementModule.createSymLink(homeUri, targetUri, onlyRead, user.getLogin()); } /** * Delete a symbolic link. * * @param homeUri * the URI of the home folder, where the link should be deleted * @return true, if deletion was successful */ public boolean deleteSymLink(URI homeUri) { return fileManagementModule.deleteSymLink(homeUri); } public File getFile(URI uri) { return fileManagementModule.getFile(uri); } /** * Deletes the slash as first character from an uri object. * * @param uri * The uri object. * @return The new uri object without first slash. */ public URI deleteFirstSlashFromPath(URI uri) { String uriString = uri.getPath(); if (uriString.startsWith("/")) { uriString = uriString.replaceFirst("/", ""); } return URI.create(uriString); } /** * Creates images files by copy of a configured source dummy image at images * source folder of given process. * * @param process * The process object. * @param numberOfNewImages * The number of images to be created. */ public void createDummyImagesForProcess(Process process, int numberOfNewImages) throws IOException, URISyntaxException { URI imagesDirectory = getSourceDirectory(process); int startValue = getNumberOfFiles(imagesDirectory) + 1; URI dummyImage = getDummyImagePath(); // Load number of digits to create valid filenames String numberOfDigits = extractNumber(ConfigCore.getParameter(ParameterCore.IMAGE_PREFIX)); for (int i = startValue; i < startValue + numberOfNewImages; i++) { copyFile(dummyImage, imagesDirectory.resolve(String.format("%0" + numberOfDigits + "d", i) + ".tif")); } } private String extractNumber(String string) { return string.replaceAll("\\D+", ""); } private URI getDummyImagePath() throws URISyntaxException, IOException { ClassLoader classloader = Thread.currentThread().getContextClassLoader(); URL dummyImage = classloader.getResource("images/dummyImage.tif"); if (Objects.nonNull(dummyImage)) { return dummyImage.toURI(); } else { throw new IOException("No dummy image found in resources!"); } } }