Coverage Summary for Class: SchemaService (org.kitodo.production.services.schema)
Class |
Class, %
|
Method, %
|
Line, %
|
SchemaService |
100%
(1/1)
|
60%
(9/15)
|
37,2%
(45/121)
|
/*
* (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.schema;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.kitodo.api.MdSec;
import org.kitodo.api.MetadataEntry;
import org.kitodo.api.dataformat.LogicalDivision;
import org.kitodo.api.dataformat.MediaVariant;
import org.kitodo.api.dataformat.PhysicalDivision;
import org.kitodo.api.dataformat.Workpiece;
import org.kitodo.api.dataformat.mets.LinkedMetsResource;
import org.kitodo.data.database.beans.Folder;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.enums.LinkingMode;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.production.helper.VariableReplacer;
import org.kitodo.production.metadata.MetadataEditor;
import org.kitodo.production.model.Subfolder;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.data.ProcessService;
import org.kitodo.production.services.dataformat.MetsService;
/**
* Service for schema manipulations.
*/
public class SchemaService {
/**
* A service that can read METS files.
*/
private final MetsService metsService = ServiceManager.getMetsService();
/**
* A service that can access processes.
*/
private final ProcessService processService = ServiceManager.getProcessService();
/**
* Temporal method for separate file conversion from ExportMets class
* (method writeMetsFile).
*
* @param workpiece
* class inside method is used
* @param process
* object
*/
public void tempConvert(Workpiece workpiece, Process process) throws IOException, DAOException, URISyntaxException {
/*
* wenn Filegroups definiert wurden, werden diese jetzt in die
* Metsstruktur übernommen
*/
// Replace all paths with the given VariableReplacer, also the file
// group paths!
VariableReplacer vp = new VariableReplacer(workpiece, process, null);
addVirtualFileGroupsToMetsMods(workpiece.getPhysicalStructure(), process);
replaceFLocatForExport(workpiece, process);
// Replace rights and digiprov entries.
set(workpiece, MdSec.RIGHTS_MD, "owner", vp.replace(process.getProject().getMetsRightsOwner()));
set(workpiece, MdSec.RIGHTS_MD, "ownerLogo", vp.replace(process.getProject().getMetsRightsOwnerLogo()));
set(workpiece, MdSec.RIGHTS_MD, "ownerSiteURL", vp.replace(process.getProject().getMetsRightsOwnerSite()));
set(workpiece, MdSec.RIGHTS_MD, "ownerContact", vp.replace(process.getProject().getMetsRightsOwnerMail()));
set(workpiece, MdSec.DIGIPROV_MD, "presentation",
vp.replace(process.getProject().getMetsDigiprovPresentation()));
set(workpiece, MdSec.DIGIPROV_MD, "reference", vp.replace(process.getProject().getMetsDigiprovReference()));
set(workpiece, MdSec.TECH_MD, "purlUrl", vp.replace(process.getProject().getMetsPurl()));
set(workpiece, MdSec.TECH_MD, "contentIDs", vp.replace(process.getProject().getMetsContentIDs()));
convertChildrenLinksForExportRecursive(workpiece.getLogicalStructure());
assignViewsFromChildrenRecursive(workpiece.getLogicalStructure());
enumerateLogicalDivisions(workpiece.getLogicalStructure(), 0, 1, false);
addLinksToParents(process, workpiece);
}
/**
* At all levels, assigns the views of the children to the included
* structural elements.
*
* @param logicalDivision
* logical division on which the recursion is
* performed
*/
private void assignViewsFromChildrenRecursive(LogicalDivision logicalDivision) {
List<LogicalDivision> children = logicalDivision.getChildren();
if (!children.isEmpty()) {
for (LogicalDivision child : children) {
assignViewsFromChildrenRecursive(child);
}
if (Objects.nonNull(logicalDivision.getType())) {
MetadataEditor.assignViewsFromChildren(logicalDivision);
}
}
}
private void set(Workpiece workpiece, MdSec domain, String key, String value) {
if (StringUtils.isNotEmpty(value)) {
MetadataEntry entry = new MetadataEntry();
entry.setKey(key);
entry.setDomain(domain);
entry.setValue(value);
workpiece.getLogicalStructure().getMetadata().add(entry);
}
}
private void addVirtualFileGroupsToMetsMods(PhysicalDivision physicalDivision, Process process) {
String canonical = ServiceManager.getFolderService().getCanonical(process, physicalDivision);
if (Objects.nonNull(canonical)) {
removeFLocatsForUnwantedUses(process, physicalDivision, canonical);
addMissingUses(process, physicalDivision, canonical);
}
for (PhysicalDivision child : physicalDivision.getChildren()) {
addVirtualFileGroupsToMetsMods(child, process);
}
}
private void replaceFLocatForExport(Workpiece workpiece, Process process)
throws URISyntaxException {
List<Folder> folders = process.getProject().getFolders();
VariableReplacer variableReplacer = new VariableReplacer(workpiece, process, null);
for (PhysicalDivision physicalDivision : workpiece.getAllPhysicalDivisions()) {
for (Entry<MediaVariant, URI> mediaFileForMediaVariant : physicalDivision.getMediaFiles().entrySet()) {
for (Folder folder : folders) {
if (folder.getFileGroup().equals(mediaFileForMediaVariant.getKey().getUse())) {
String mediaFileWithPath = mediaFileForMediaVariant.getValue().toString();
String mediaFilename = FilenameUtils.getName(mediaFileWithPath);
String mediaFile = variableReplacer.containsFiles(folder.getUrlStructure())
? variableReplacer.replaceWithFilename(folder.getUrlStructure(), mediaFileWithPath)
: variableReplacer.replace(folder.getUrlStructure() + mediaFilename);
mediaFileForMediaVariant.setValue(new URI(mediaFile));
}
}
}
}
}
/**
* If the physical division contains a media variant that is unknown, has linking
* mode NO or has linking mode EXISTING but the file does not exist, remove
* it.
*/
private void removeFLocatsForUnwantedUses(Process process,
PhysicalDivision physicalDivision,
String canonical) {
for (Iterator<Entry<MediaVariant, URI>> mediaFilesForMediaVariants = physicalDivision.getMediaFiles().entrySet()
.iterator(); mediaFilesForMediaVariants.hasNext();) {
Entry<MediaVariant, URI> mediaFileForMediaVariant = mediaFilesForMediaVariants.next();
String use = mediaFileForMediaVariant.getKey().getUse();
Optional<Folder> optionalFolderForUse = process.getProject().getFolders().parallelStream()
.filter(folder -> use.equals(folder.getFileGroup())).findAny();
if (optionalFolderForUse.isEmpty()
|| optionalFolderForUse.get().getLinkingMode().equals(LinkingMode.NO)
|| (optionalFolderForUse.get().getLinkingMode().equals(LinkingMode.EXISTING)
&& new Subfolder(process, optionalFolderForUse.get()).getURIIfExists(canonical)
.isPresent())) {
mediaFilesForMediaVariants.remove();
}
}
}
/**
* If the physical division is missing a variant that has linking mode ALL or has
* linking mode EXISTING and the file does exist, add it.
*/
private void addMissingUses(Process process, PhysicalDivision physicalDivision,
String canonical) {
for (Folder folder : process.getProject().getFolders()) {
Subfolder useFolder = new Subfolder(process, folder);
if (physicalDivision.getMediaFiles().entrySet().parallelStream().map(Entry::getKey).map(MediaVariant::getUse)
.noneMatch(use -> use.equals(folder.getFileGroup())) && (folder.getLinkingMode().equals(LinkingMode.ALL)
|| (folder.getLinkingMode().equals(LinkingMode.EXISTING) && useFolder.getURIIfExists(canonical).isPresent()))) {
addUse(useFolder, canonical, physicalDivision);
}
}
}
/**
* Adds a use to a physical division.
*
* @param subfolder
* subfolder for the use
* @param canonical
* the canonical part of the file name of the media file
* @param physicalDivision
* physical division to add to
*/
private void addUse(Subfolder subfolder, String canonical, PhysicalDivision physicalDivision) {
MediaVariant mediaVariant = new MediaVariant();
mediaVariant.setUse(subfolder.getFolder().getFileGroup());
mediaVariant.setMimeType(subfolder.getFolder().getMimeType());
URI mediaFile = subfolder.getRelativeFilePath(canonical);
physicalDivision.getMediaFiles().put(mediaVariant, mediaFile);
}
/**
* Replaces internal links in child structure elements with a publicly
* resolvable link. Checks whether the linked process has not yet been
* exported, in which case the link from the parental list will be deleted.
*
* @param structure
* current structure
* @return whether the current structure shall be deleted
*/
private boolean convertChildrenLinksForExportRecursive(LogicalDivision structure) throws DAOException, IOException {
LinkedMetsResource link = structure.getLink();
if (Objects.nonNull(link)) {
int linkedProcessId = processService.processIdFromUri(link.getUri());
Process process = processService.getById(linkedProcessId);
if (!process.isExported()) {
return true;
}
setLinkForExport(structure, process);
copyLabelAndOrderlabel(process, structure);
}
for (Iterator<LogicalDivision> iterator = structure.getChildren().iterator(); iterator.hasNext();) {
if (convertChildrenLinksForExportRecursive(iterator.next())) {
iterator.remove();
}
}
return false;
}
private int enumerateLogicalDivisions(LogicalDivision logicalDivision, int elementCount,
int journalIssueCount, boolean journalIssue) {
boolean untyped = Objects.isNull(logicalDivision.getType());
logicalDivision.setOrder(untyped ? 0 : (journalIssue ? journalIssueCount++ : elementCount));
for (int i = 0; i < logicalDivision.getChildren().size(); i++) {
journalIssueCount = enumerateLogicalDivisions(logicalDivision.getChildren().get(i), i + 1,
journalIssueCount, untyped);
}
return journalIssueCount;
}
private void addLinksToParents(Process process, Workpiece workpiece) throws IOException {
Process parentProcess = process.getParent();
while (Objects.nonNull(parentProcess)) {
addParentLinkForExport(workpiece, parentProcess);
parentProcess = parentProcess.getParent();
}
}
private void addParentLinkForExport(Workpiece workpiece, Process parent) throws IOException {
LogicalDivision linkHolder = new LogicalDivision();
linkHolder.setLink(new LinkedMetsResource());
setLinkForExport(linkHolder, parent);
linkHolder.getChildren().add(workpiece.getLogicalStructure());
copyLabelAndOrderlabel(parent, linkHolder);
workpiece.setLogicalStructure(linkHolder);
}
private void setLinkForExport(LogicalDivision structure, Process process) throws IOException {
LinkedMetsResource link = structure.getLink();
link.setLoctype("URL");
String uriWithVariables = process.getProject().getMetsPointerPath();
Workpiece workpiece = uriWithVariables.contains("$(meta.") ? ServiceManager.getMetsService()
.loadWorkpiece(ServiceManager.getProcessService().getMetadataFileUri(process)) : null;
VariableReplacer variableReplacer = new VariableReplacer(workpiece, process, null);
String linkUri = variableReplacer.replace(uriWithVariables);
link.setUri(URI.create(linkUri));
structure.setType(ServiceManager.getProcessService().getBaseType(process));
}
private void copyLabelAndOrderlabel(Process source, LogicalDivision destination) throws IOException {
URI sourceMetadataUri = processService.getMetadataFileUri(source);
LogicalDivision sourceRoot = metsService.loadWorkpiece(sourceMetadataUri).getLogicalStructure();
if (Objects.isNull(destination.getLabel())) {
destination.setLabel(sourceRoot.getLabel());
}
if (Objects.isNull(destination.getOrderlabel())) {
destination.setOrderlabel(sourceRoot.getOrderlabel());
}
}
}