Coverage Summary for Class: TitleRecordLinkTab (org.kitodo.production.forms.createprocess)

Class Class, % Method, % Line, %
TitleRecordLinkTab 100% (1/1) 33,3% (7/21) 22,3% (27/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.forms.createprocess;
 
 import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale.LanguageRange;
 import java.util.Objects;
 import java.util.Optional;
 
 import javax.faces.model.SelectItem;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.kitodo.api.dataeditor.rulesetmanagement.FunctionalMetadata;
 import org.kitodo.api.dataeditor.rulesetmanagement.RulesetManagementInterface;
 import org.kitodo.api.dataeditor.rulesetmanagement.StructuralElementViewInterface;
 import org.kitodo.api.dataformat.LogicalDivision;
 import org.kitodo.api.dataformat.Workpiece;
 import org.kitodo.data.database.beans.Process;
 import org.kitodo.data.database.exceptions.DAOException;
 import org.kitodo.data.exceptions.DataException;
 import org.kitodo.production.dto.ProcessDTO;
 import org.kitodo.production.helper.Helper;
 import org.kitodo.production.metadata.MetadataEditor;
 import org.kitodo.production.services.ServiceManager;
 import org.kitodo.production.services.data.ProcessService;
 import org.kitodo.production.services.dataformat.MetsService;
 import org.omnifaces.util.Ajax;
 import org.primefaces.model.DefaultTreeNode;
 import org.primefaces.model.TreeNode;
 
 /**
  * Backing bean for the title record link tab.
  */
 public class TitleRecordLinkTab {
     private static final Logger logger = LogManager.getLogger(TitleRecordLinkTab.class);
 
     static final String INSERTION_TREE = "editForm:processFromTemplateTabView:insertionTree";
     private static final MetsService metsService = ServiceManager.getMetsService();
     private static final ProcessService processService = ServiceManager.getProcessService();
 
     /**
      * Maximum number of search hits to show.
      */
     private static final int MAXIMUM_NUMBER_OF_HITS = 10;
 
     private final CreateProcessForm createProcessForm;
 
     /**
      * The user-selected parent process.
      */
     private String chosenParentProcess = null;
 
     /**
      * Specifies whether an indication of further hits is visible.
      */
     private boolean indicationOfMoreHitsVisible = false;
 
     /**
      * Elements from which a parent process can be selected.
      */
     private List<SelectItem> possibleParentProcesses = Collections.emptyList();
 
     /**
      * Tree with the logical structure of the workpiece and elements indicating the
      * possible insertion positions.
      */
     private TreeNode logicalStructure = new DefaultTreeNode();
 
     /**
      * The user’s search for parent processes.
      */
     private String searchQuery = "";
 
     /**
      * The list of possible insertion positions.
      */
     private List<SelectItem> selectableInsertionPositions = Collections.emptyList();
 
     /**
      * The user-selected insertion position.
      */
     private String selectedInsertionPosition = null;
 
     /**
      * Process selected as parent.
      */
     private Process titleRecordProcess = null;
 
     /**
      * Creates a new data object underlying the title record link tab.
      *
      * @param createProcessForm
      *            CreateProcessForm containing the object
      */
     public TitleRecordLinkTab(CreateProcessForm createProcessForm) {
         this.createProcessForm = createProcessForm;
     }
 
     /**
      * Selects a parent process and builds the tree with the logical structure of the
      * selected process and the possible insertion positions.
      */
     public void chooseParentProcess() {
         if (StringUtils.isBlank(chosenParentProcess)) {
             logicalStructure = new DefaultTreeNode();
             titleRecordProcess = null;
         } else {
             try {
                 titleRecordProcess = ServiceManager.getProcessService().getById(Integer.valueOf(chosenParentProcess));
                 createInsertionPositionSelectionTree();
             } catch (DAOException | DataException | IOException e) {
                 Helper.setErrorMessage("errorLoadingOne",
                         new Object[] {possibleParentProcesses.parallelStream()
                                 .filter(selectItem -> selectItem.getValue().equals(chosenParentProcess)).findAny()
                                 .orElse(new SelectItem(null, null)).getLabel(), chosenParentProcess },
                         logger, e);
             }
         }
     }
 
     /**
      * Sets up the variables for the tree for the insertion position selection.
      *
      * @throws IOException
      *             if the METS file cannot be read
      */
     public void createInsertionPositionSelectionTree() throws DAOException, DataException, IOException {
         if (Objects.isNull(titleRecordProcess)) {
             return;
         }
         URI uri = ServiceManager.getProcessService().getMetadataFileUri(titleRecordProcess);
         Workpiece workpiece = metsService.loadWorkpiece(uri);
 
         RulesetManagementInterface ruleset = ServiceManager.getRulesetService()
                 .openRuleset(titleRecordProcess.getRuleset());
         String metadataLanguage = ServiceManager.getUserService().getCurrentUser().getMetadataLanguage();
         List<LanguageRange> priorityList = LanguageRange.parse(metadataLanguage.isEmpty() ? "en" : metadataLanguage);
 
         selectableInsertionPositions = new LinkedList<>();
         logicalStructure = new DefaultTreeNode();
         createInsertionPositionSelectionTreeRecursive("", workpiece.getLogicalStructure(), logicalStructure, ruleset,
             priorityList);
         logicalStructure.setExpanded(true);
 
         if (selectableInsertionPositions.size() > 0) {
             selectedInsertionPosition = (String) ((LinkedList<SelectItem>) selectableInsertionPositions).getLast()
                     .getValue();
         } else {
             selectedInsertionPosition = null;
             Helper.setErrorMessage("createProcessForm.titleRecordLinkTab.noInsertionPosition");
         }
     }
 
     /**
      * Recursively builds the tree for the insertion position selection.
      *
      * @param positionPrefix
      *            A string with comma-delimited specification of the levels that
      *            have already been traversed. Initially empty.
      * @param currentLogicalDivision
      *            Logical division for whom the tree is being created. Initially
      *            the logical structure of the workpiece.
      * @param parentNode
      *            Parent node of the tree structure to add to. This is
      *            initialized with a {@link DefaultTreeNode} which is not
      *            displayed
      * @param ruleset
      *            the current ruleset
      * @param priorityList
      *            the user’s metadata language priority list
      */
     private void createInsertionPositionSelectionTreeRecursive(String positionPrefix,
             LogicalDivision currentLogicalDivision, TreeNode parentNode,
             RulesetManagementInterface ruleset, List<LanguageRange> priorityList) throws IOException, DAOException, DataException {
 
         String type;
         List<String> tooltip = Collections.emptyList();
         if (Objects.isNull(currentLogicalDivision.getLink())) {
             type = currentLogicalDivision.getType();
         } else {
             ProcessService processService = ServiceManager.getProcessService();
             int processIdFromUri = processService.processIdFromUri(currentLogicalDivision.getLink().getUri());
             Process linkedProcess = processService.getById(processIdFromUri);
             type = processService.getBaseType(processIdFromUri);
             tooltip = getToolTip(ruleset, linkedProcess);
         }
 
         StructuralElementViewInterface currentStructuralElementView = ruleset.getStructuralElementView(type,
             createProcessForm.getAcquisitionStage(), priorityList);
 
         TreeNode logicalDivisionNode = new InsertionPositionSelectionTreeNode(parentNode,
                 currentStructuralElementView.getLabel(), tooltip);
 
         boolean linkingAllowedHere = Objects.isNull(currentLogicalDivision.getLink())
                 && currentStructuralElementView.getAllowedSubstructuralElements()
                         .containsKey(createProcessForm.getProcessDataTab().getDocType());
 
         if (linkingAllowedHere) {
             new InsertionPositionSelectionTreeNode(logicalDivisionNode, selectableInsertionPositions.size());
             selectableInsertionPositions.add(new SelectItem(positionPrefix.concat("0"), null));
         }
 
         List<LogicalDivision> children = currentLogicalDivision.getChildren();
         for (int index = 0; index < children.size(); index++) {
 
             createInsertionPositionSelectionTreeRecursive(
                 positionPrefix + index + 1 + MetadataEditor.INSERTION_POSITION_SEPARATOR, children.get(index),
                 logicalDivisionNode, ruleset, priorityList);
 
             if (linkingAllowedHere) {
                 new InsertionPositionSelectionTreeNode(logicalDivisionNode,
                         selectableInsertionPositions.size());
                 selectableInsertionPositions.add(new SelectItem(positionPrefix + (index + 1), null));
             }
         }
     }
 
     /**
      * Determines the overlay text for a node of the tree.
      *
      * @param ruleset
      *            Ruleset of the process
      * @param linkedProcess
      *            Linked child process
      * @return text to be displayed
      * @throws IOException
      *             if the METS file cannot be read
      */
     private List<String> getToolTip(RulesetManagementInterface ruleset, Process linkedProcess) throws IOException {
 
         Collection<String> summaryKeys = ruleset.getFunctionalKeys(FunctionalMetadata.DISPLAY_SUMMARY);
         List<String> toolTip = new ArrayList<>();
         if (!summaryKeys.isEmpty()) {
 
             Workpiece workpiece = metsService.loadWorkpiece(processService.getMetadataFileUri(linkedProcess));
             LogicalDivision logicalStructure = workpiece.getLogicalStructure();
 
             final String metadataLanguage = ServiceManager.getUserService().getCurrentUser().getMetadataLanguage();
             List<LanguageRange> priorityList = Locale.LanguageRange.parse(metadataLanguage);
 
             for (String key : summaryKeys) {
                 String value = MetadataEditor.getMetadataValue(logicalStructure, key);
 
                 if (Objects.nonNull(value)) {
                     Optional<String> label = ruleset.getTranslationForKey(key, priorityList);
                     toolTip.add(label.orElse(key) + ": " + value);
                 }
             }
         }
 
         if (toolTip.isEmpty()) {
             toolTip.add(linkedProcess.toString());
         }
 
         return toolTip;
     }
 
 
     /**
      * Returns the HTML identifier of the selected parent process.
      *
      * @return the identifier of the selected parent process
      */
     public String getChosenParentProcess() {
         return chosenParentProcess;
     }
 
     /**
      * Sets the HTML identifier of the selected parent process.
      *
      * @param chosenParentProcess
      *            identifier to set
      */
     public void setChosenParentProcess(String chosenParentProcess) {
         this.chosenParentProcess = chosenParentProcess;
     }
 
     /**
      * Search for possible parent processes and fill with the result the hit
      * selection. If there are more than the maximum number of hits defined as a
      * constant above, the corresponding message is displayed.
      */
     public void searchForParentProcesses() {
         logicalStructure = new DefaultTreeNode();
         selectableInsertionPositions = Collections.emptyList();
         selectedInsertionPosition = null;
         if (searchQuery.trim().isEmpty()) {
             Helper.setMessage("createProcessForm.titleRecordLinkTab.searchButtonClick.empty");
             return;
         }
         try {
             List<ProcessDTO> processes = ServiceManager.getProcessService().findLinkableParentProcesses(searchQuery,
                 createProcessForm.getProject().getId(), createProcessForm.getTemplate().getRuleset().getId());
             if (processes.isEmpty()) {
                 Helper.setMessage("createProcessForm.titleRecordLinkTab.searchButtonClick.noHits");
             }
             indicationOfMoreHitsVisible = processes.size() > MAXIMUM_NUMBER_OF_HITS;
             possibleParentProcesses = new ArrayList<>();
             for (ProcessDTO process : processes.subList(0, Math.min(processes.size(), MAXIMUM_NUMBER_OF_HITS))) {
                 possibleParentProcesses.add(new SelectItem(process.getId().toString(), process.getTitle()));
             }
         } catch (DataException e) {
             Helper.setErrorMessage("createProcessForm.titleRecordLinkTab.searchButtonClick.error", e.getMessage(),
                 logger, e);
             indicationOfMoreHitsVisible = false;
             possibleParentProcesses = Collections.emptyList();
         }
     }
 
     /**
      * Returns the search query typed by the user.
      *
      * @return the search query
      */
     public String getSearchQuery() {
         return searchQuery;
     }
 
     /**
      * Sets the search query typed by the user.
      *
      * @param searchQuery
      *            search query to set
      */
     public void setSearchQuery(String searchQuery) {
         this.searchQuery = searchQuery;
     }
 
     /**
      * Returns the HTML identifier of the selected insertion position.
      *
      * @return the selected insertion position
      */
     public String getSelectedInsertionPosition() {
         return selectedInsertionPosition;
     }
 
     /**
      * Sets the HTML identifier of the selected insertion position.
      *
      * @param selectedInsertionPosition
      *            identifier to set
      */
     public void setSelectedInsertionPosition(String selectedInsertionPosition) {
         this.selectedInsertionPosition = selectedInsertionPosition;
     }
 
     /**
      * Returns whether the hint that there were more hits is visible.
      *
      * @return whether the hint is visible
      */
     public boolean isIndicationOfMoreHitsVisible() {
         return indicationOfMoreHitsVisible;
     }
 
     /**
      * Returns the list of selectors for selecting a parent process.
      *
      * @return the list of selectors
      */
     public List<SelectItem> getPossibleParentProcesses() {
         return possibleParentProcesses;
     }
 
     /**
      * Set possibleParentProcesses.
      *
      * @param possibleParentProcesses as list
      */
     public void setPossibleParentProcesses(List<SelectItem> possibleParentProcesses) {
         this.possibleParentProcesses = possibleParentProcesses;
     }
 
     /**
      * Returns the tree containing the logical structure of the selected parent
      * process and the possible insert positions.
      *
      * @return the tree structure for selecting the insertion position
      */
     public TreeNode getLogicalStructure() {
         return logicalStructure;
     }
 
     /**
      * Returns the list of selection items for selecting an insertion position.
      *
      * @return selection elements for selecting an insertion position
      */
     public List<SelectItem> getSelectableInsertionPositions() {
         return selectableInsertionPositions;
     }
 
     /**
      * Returns the process of the selected title record.
      *
      * @return the process of the selected title record
      */
     public Process getTitleRecordProcess() {
         return titleRecordProcess;
     }
 
     /**
      * Sets the process of the selected title record.
      *
      * @param titleRecordProcess the process of the selected title record
      */
     public void setTitleRecordProcess(Process titleRecordProcess) {
         this.titleRecordProcess = titleRecordProcess;
     }
 
     /**
      * Set given process "parentProcess" as parent title record of new process.
      * @param parentProcess process to set as parent title record
      */
     public void setParentAsTitleRecord(Process parentProcess) {
         createProcessForm.setEditActiveTabIndex(CreateProcessForm.TITLE_RECORD_LINK_TAB_INDEX);
         ArrayList<SelectItem> parentCandidates = new ArrayList<>();
         parentCandidates.add(new SelectItem(parentProcess.getId().toString(), parentProcess.getTitle()));
         createProcessForm.getTitleRecordLinkTab().setPossibleParentProcesses(parentCandidates);
         createProcessForm.getTitleRecordLinkTab().setChosenParentProcess((String)parentCandidates.get(0).getValue());
         createProcessForm.getTitleRecordLinkTab().chooseParentProcess();
         Ajax.update(INSERTION_TREE);
     }
 }