Coverage Summary for Class: CalendarForm (org.kitodo.production.forms)
Class |
Class, %
|
Method, %
|
Line, %
|
CalendarForm |
0%
(0/1)
|
0%
(0/60)
|
0%
(0/242)
|
/*
* (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;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.Month;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import javax.naming.ConfigurationException;
import javax.xml.transform.TransformerException;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kitodo.config.ConfigCore;
import org.kitodo.config.enums.ParameterCore;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.exceptions.DataException;
import org.kitodo.exceptions.DoctypeMissingException;
import org.kitodo.exceptions.ProcessGenerationException;
import org.kitodo.production.forms.createprocess.ProcessDetail;
import org.kitodo.production.forms.createprocess.ProcessTextMetadata;
import org.kitodo.production.helper.Helper;
import org.kitodo.production.helper.XMLUtils;
import org.kitodo.production.helper.tasks.GeneratesNewspaperProcessesThread;
import org.kitodo.production.helper.tasks.TaskManager;
import org.kitodo.production.model.bibliography.course.Block;
import org.kitodo.production.model.bibliography.course.Cell;
import org.kitodo.production.model.bibliography.course.Course;
import org.kitodo.production.model.bibliography.course.Granularity;
import org.kitodo.production.model.bibliography.course.IndividualIssue;
import org.kitodo.production.model.bibliography.course.Issue;
import org.kitodo.production.model.bibliography.course.metadata.CountableMetadata;
import org.kitodo.production.process.NewspaperProcessesGenerator;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.calendar.CalendarService;
import org.kitodo.production.services.data.ImportService;
import org.primefaces.PrimeFaces;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.file.UploadedFile;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
/**
* The class CalendarForm provides the screen logic for a JSF calendar editor to
* enter the course of appearance of a newspaper.
*/
@Named("CalendarForm")
@ViewScoped
public class CalendarForm implements Serializable {
private static final Logger logger = LogManager.getLogger(CalendarForm.class);
private static final String BLOCK = "calendar.block.";
private static final String BLOCK_NEGATIVE = BLOCK + "negative";
private static final String UPLOAD_ERROR = "calendar.upload.error";
private static final String REDIRECT_PARAMETER = "faces-redirect=true";
private static final String DEFAULT_REFERER = "processes?" + REDIRECT_PARAMETER;
private static final String TASK_MANAGER_REFERER = "system.jsf?tabIndex=0&" + REDIRECT_PARAMETER;
private static final Integer[] MONTHS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
/**
* This is a regular expression to parse date inputs in a flexible way.
*/
private static final Pattern FLEXIBLE_DATE = Pattern.compile("\\D*(\\d+)\\D+(\\d+)\\D+(\\d+)\\D*");
/**
* The constant field issueColours holds the colors used to represent the
* issues in the calendar editor.
*/
private static String[] issueColours;
/**
* The constant field START_RELATION hold the date the course of publication
* of the the German-language “Relation aller Fürnemmen und gedenckwürdigen
* Historien”, which is often recognized as the first newspaper, began. If
* the user tries to create a block before that date, a hint will be shown.
*/
private static final LocalDate START_RELATION = LocalDate.of(1605, 9, 12);
private String referer = DEFAULT_REFERER;
private Granularity granularity = Granularity.ISSUES;
private int numberOfPagesPerIssue = 0;
protected int yearShowing = 1979;
private UploadedFile uploadedFile;
private LocalDate selectedDate;
private Block selectedBlock = null;
/**
* The field course holds the course of appearance currently under edit by
* this calendar form instance.
*/
protected Course course;
/**
* The constant field today hold the date of today. Reading the system clock
* requires much synchronisation throughout the JVM and is therefore only
* done once on form creation.
*/
private final LocalDate today = LocalDate.now();
private static Integer parentId;
private String activeIndexes = "0";
/**
* Empty constructor. Creates a new form without yet any data.
*
* <p>
* The issue color presets are samples which have been chosen to provide
* distinguishability also for users with red-green color vision deficiency.
* Arbitrary colors can be defined in kitodo_config.properties by setting
* the property “issue.colours”.
*/
public CalendarForm() {
issueColours = ConfigCore.getParameterOrDefaultValue(ParameterCore.ISSUE_COLOURS).split(";");
course = new Course();
}
/**
* Gets activeIndexes.
*
* @return value of activeIndexes
*/
public String getActiveIndexes() {
return activeIndexes;
}
/**
* Sets activeIndexes.
*
* @param activeIndexes value of activeIndexes
*/
public void setActiveIndexes(String activeIndexes) {
this.activeIndexes = activeIndexes;
}
/**
* Get referer.
*
* @return value of referer
*/
public String getReferer() {
return referer;
}
/**
* Set referer.
*
* @param referer as java.lang.String
*/
public void setReferer(String referer) {
if (Objects.nonNull(referer) && !referer.isEmpty()) {
this.referer = referer;
}
}
/**
* Set parent processId.
*
* @param parentId as java.lang.Integer
*/
public void setParentId(Integer parentId) {
if (Objects.nonNull(parentId)) {
this.parentId = parentId;
}
}
/**
* Gets parentId.
*
* @return value of parentId
*/
public static Integer getParentId() {
return parentId;
}
/**
* Get all possible granularities.
*
* @return list of Granularity objects
*/
public List<Granularity> getGranularities() {
return Arrays.asList(Granularity.values());
}
/**
* Get granularity.
*
* @return value of granularity
*/
public Granularity getGranularity() {
return granularity;
}
/**
* Set granularity.
*
* @param granularity as org.kitodo.production.model.bibliography.course.Granularity
*/
public void setGranularity(Granularity granularity) {
this.granularity = granularity;
course.splitInto(granularity);
if (Objects.nonNull(PrimeFaces.current()) && Objects.nonNull(FacesContext.getCurrentInstance())) {
PrimeFaces.current().ajax().update("createProcessesConfirmDialog");
}
}
/**
* Get array representing the months of a year.
*
* @return value of MONTHS
*/
public static Integer[] getMonths() {
return MONTHS;
}
/**
* Get the currently displayed year.
*
* @return the year to be displayed as java.lang.String
*/
public String getYear() {
return Integer.toString(yearShowing);
}
/**
* Set the currently displayed year.
*
* @param year to be displayed as java.lang.String
*/
public void setYear(String year) {
yearShowing = Integer.parseInt(year);
}
/**
* Display the previous year in the calendar.
*/
public void previousYear() {
yearShowing -= 1;
}
/**
* Display the next year in the calendar.
*/
public void nextYear() {
yearShowing += 1;
}
/**
* Get estimated number of pages per issue.
*
* @return value of numberOfPagesPerIssue
*/
public int getNumberOfPagesPerIssue() {
return numberOfPagesPerIssue;
}
/**
* Set estimated number of pages per issue.
*
* @param numberOfPagesPerIssue as int
*/
public void setNumberOfPagesPerIssue(int numberOfPagesPerIssue) {
this.numberOfPagesPerIssue = numberOfPagesPerIssue;
}
/**
* Get the number of pages of every process for the chosen granularity.
* Formatted as String with one decimal place.
*
* @return number of images as java.lang.String
*/
public String getNumberOfPagesPerProcessFormatted() {
DecimalFormat decimalFormat = new DecimalFormat("#.#");
return decimalFormat.format(getNumberOfPagesPerProcess());
}
/**
* Get the number of pages of every process for the chosen granularity.
*
* @return number of pages as long
*/
public double getNumberOfPagesPerProcess() {
return course.countIndividualIssues() / ((double) Math.max(course.getNumberOfProcesses(), 1)) * numberOfPagesPerIssue;
}
/**
* The function checkBlockPlausibility compares the dates entered against
* some plausibility assumptions and sets hints otherwise.
*/
public void checkBlockPlausibility(Block block) {
LocalDate firstAppearance = block.getFirstAppearance();
LocalDate lastAppearance = block.getLastAppearance();
if (Objects.nonNull(firstAppearance) && Objects.nonNull(lastAppearance)) {
if (firstAppearance.plusYears(100).isBefore(lastAppearance)) {
Helper.setMessage(BLOCK + "long");
}
if (firstAppearance.isAfter(lastAppearance)) {
Helper.setErrorMessage(BLOCK_NEGATIVE);
}
if (firstAppearance.isBefore(START_RELATION)) {
Helper.setMessage(BLOCK + "firstAppearance.early");
}
if (firstAppearance.isAfter(today)) {
Helper.setMessage(BLOCK + "firstAppearance.fiction");
}
if (lastAppearance.isBefore(START_RELATION)) {
Helper.setMessage(BLOCK + "lastAppearance.early");
}
if (lastAppearance.isAfter(today)) {
Helper.setMessage(BLOCK + "lastAppearance.fiction");
}
this.setYear(String.valueOf(firstAppearance.getYear()));
if (Objects.nonNull(PrimeFaces.current()) && Objects.nonNull(FacesContext.getCurrentInstance())) {
PrimeFaces.current().ajax().update("editForm:calendarTabView:calendarDetailsLayout");
}
}
}
/**
* Change whether the selected issue appeared on the selected date.
* Depending on the regular interval of appearance this will change the additions and exclusions for this issue.
*
* @param selectedIssue issue to be modified
* @param selectedDate date for which the issue will be modified
*/
public void changeMatch(Issue selectedIssue, LocalDate selectedDate) {
if (selectedIssue.isMatch(selectedDate) && selectedIssue.getAdditions().contains(selectedDate)) {
selectedIssue.removeAddition(selectedDate);
} else if (selectedIssue.isMatch(selectedDate) && !selectedIssue.getAdditions().contains(selectedDate)) {
selectedIssue.addExclusion(selectedDate);
} else if (!selectedIssue.isMatch(selectedDate) && selectedIssue.getExclusions().contains(selectedDate)) {
selectedIssue.removeExclusion(selectedDate);
} else if (!selectedIssue.isMatch(selectedDate) && !selectedIssue.getExclusions().contains(selectedDate)) {
selectedIssue.addAddition(selectedDate);
}
}
/**
* Creates and adds a copy of the currently
* showing block.
*/
public void copyBlock(Block block) {
Block copy = block.clone(course);
LocalDate lastAppearance = course.getLastAppearance();
if (Objects.nonNull(lastAppearance)) {
LocalDate firstAppearance = lastAppearance.plusDays(1);
copy.setFirstAppearance(firstAppearance);
copy.setLastAppearance(firstAppearance);
course.add(copy);
navigate(copy);
}
}
/**
* The function is executed if the user clicks the action
* link to “export” the calendar data. If the course of appearance doesn’t
* yet contain generated processes—which is always the case, except that the
* user just came from uploading a data file and didn’t change anything
* about it—process data will be generated. Then an XML file will be made
* out of it and sent to the user’s browser. If the granularity was
* temporarily added, it will be removed afterwards so that the user will
* not be presented with the option to generate processes “as imported” if
* he or she never ran an import before.
*
* <p>
* Note: The process data will be generated with a granularity of “days”
* (each day forms one process). This setting can be changed later after the
* data has been re-imported, but it will remain if the user uploads the
* saved data and then proceeds right to the next page and creates processes
* with the granularity “as imported”. However, since this is possible
* and—as to our knowledge in late 2014, when this was written—this is the
* best option of all, this default has been chosen here.
*/
public StreamedContent download() {
boolean granularityWasTemporarilyAdded = false;
try {
if (Objects.isNull(course) || course.countIndividualIssues() == 0) {
Helper.setErrorMessage("errorDataIncomplete", "calendar.isEmpty");
return null;
}
if (course.getNumberOfProcesses() == 0) {
granularityWasTemporarilyAdded = true;
course.splitInto(Granularity.DAYS);
}
byte[] data = XMLUtils.documentToByteArray(course.toXML(), 4);
return DefaultStreamedContent.builder().stream(() -> new ByteArrayInputStream(data))
.contentType("application/xml").name("newspaper.xml").build();
} catch (TransformerException e) {
Helper.setErrorMessage("granularity.download.error", "errorTransformerException", logger, e);
} catch (IOException e) {
Helper.setErrorMessage("granularity.download.error", e.getLocalizedMessage(), logger, e);
} finally {
if (granularityWasTemporarilyAdded) {
course.clearProcesses();
}
}
return null;
}
/**
* Returns whether the calendar editor is in mint
* condition, i.e. there is no block defined yet, as read-only property
* “blank”.
*
* <p>
* Side note: “empty” is a reserved word in JSP and cannot be used as
* property name.
*
* @return whether there is no block yet
*/
public boolean getBlank() {
return course.isEmpty();
}
/**
* Returns the data required to build the
* calendar sheet as read-only property "calendarSheet". The outer list
* contains 31 entries, each representing a row of the calendar (the days
* 1−31), each line then contains 12 cells representing the months. This is
* due to HTML table being produced line by line.
*
* @return the table cells to build the calendar sheet
*/
public List<List<Cell>> getCalendarSheet() {
List<List<Cell>> calendarSheet = getEmptySheet();
populateByCalendar(calendarSheet);
return calendarSheet;
}
/**
* The function will return the course created with this editor
* as read-only property "course" to pass it to the next form.
*
* @return the course of appearance data model
*/
public Course getCourse() {
return course;
}
/**
* Builds the empty calendar sheet with 31 rows
* of twelve cells with empty objects of type Cell().
*
* @return an empty calendar sheet
*/
private List<List<Cell>> getEmptySheet() {
List<List<Cell>> emptySheet = new ArrayList<>(31);
for (int day = 1; day <= 31; day++) {
ArrayList<Cell> row = new ArrayList<>(12);
for (int month = 1; month <= 12; month++) {
row.add(new Cell());
}
emptySheet.add(row);
}
return emptySheet;
}
/**
* The function is the getter method for the property
* "uploadedFile" which is write-only, however Faces requires is.
*
* @return always null
*/
public UploadedFile getUploadedFile() {
return null;
}
/**
* Alters the year the calendar sheet is shown for so
* that something of the current block is visible to prevent the user from
* needing to click through centuries manually to get there.
*/
protected void navigate(Block block) {
try {
if (yearShowing > block.getLastAppearance().getYear()) {
yearShowing = block.getLastAppearance().getYear();
}
if (yearShowing < block.getFirstAppearance().getYear()) {
yearShowing = block.getFirstAppearance().getYear();
}
} catch (NullPointerException e) {
Helper.setErrorMessage(e.getLocalizedMessage(), logger, e);
}
}
/**
* Tries to interpret a string entered by the user as a date as flexible as
* possible. Supports two-digit years and imperial date field order
* (month/day/year). In case of flexible interpretations, hints will be
* displayed to put the user on the right track what happened to his input.
*
* <p>
* If the user clicks the link to upload a course of appearance file, no
* warning message shall show. Therefore an alternate white-space character
* (U+00A0) will be appended to the value string by Javascript on the user
* side because the setter methods will be called by Faces before the link
* action will be executed, but we want to skip the error message generation
* in that case, too.
*
* @param value
* value entered by the user
* @param input
* input element, one of "firstAppearance" or "lastAppearance"
* @return the date if found, or null otherwise
*/
private LocalDate parseDate(String value, String input) {
Matcher dateParser = FLEXIBLE_DATE.matcher(value);
int[] numbers = new int[3];
if (dateParser.matches()) {
for (int i = 0; i < 3; i++) {
numbers[i] = Integer.parseInt(dateParser.group(i + 1));
}
if (numbers[2] < 100) {
numbers[2] += 100 * Math.floor((double) today.getYear() / 100);
if (numbers[2] > today.getYear()) {
numbers[2] -= 100;
}
Helper.setMessage(Helper.getTranslation(BLOCK + input + ".yearCompleted",
dateParser.group(3), Integer.toString(numbers[2])));
}
try {
return LocalDate.of(numbers[2], numbers[1], numbers[0]);
} catch (DateTimeException invalidDate) {
try {
LocalDate swapped = LocalDate.of(numbers[2], numbers[0], numbers[1]);
Helper.setMessage(BLOCK + input + ".swapped");
return swapped;
} catch (DateTimeException stillInvalid) {
Helper.setErrorMessage(invalidDate.getLocalizedMessage(), logger, stillInvalid);
}
}
}
if (!value.contains("\u00A0")) {
Helper.setErrorMessage(BLOCK + input + ".invalid");
}
return null;
}
/**
* Populates an empty calendar sheet by
* iterating on LocalDate.
*
* @param sheet
* calendar sheet to populate
*/
protected void populateByCalendar(List<List<Cell>> sheet) {
Map<Integer, List<Issue>> issuesMap = new HashMap<>();
Block currentBlock = null;
LocalDate nextYear = LocalDate.of(yearShowing + 1, Month.JANUARY, 1);
for (LocalDate date = LocalDate.of(yearShowing, Month.JANUARY, 1); date
.isBefore(nextYear); date = date.plusDays(1)) {
Cell cell = sheet.get(date.getDayOfMonth() - 1).get(date.getMonthValue() - 1);
cell.setDate(date);
if (Objects.isNull(currentBlock) || !currentBlock.isMatch(date)) {
currentBlock = course.isMatch(date);
}
if (Objects.isNull(currentBlock)) {
cell.setOnBlock(false);
} else {
Integer hashCode = currentBlock.hashCode();
if (!issuesMap.containsKey(hashCode)) {
issuesMap.put(hashCode, currentBlock.getIssues());
}
cell.setIssues(issuesMap.get(hashCode));
}
}
}
/**
* Add a block to the course.
*/
public void addBlock() {
course.add(new Block(course));
}
/**
* Remove block.
*
* @param block
* The Block to be removed from the course.
*/
public void removeBlock(Block block) {
int index = course.indexOf(block);
course.remove(block);
if (index > 0) {
index--;
}
if (course.size() > 0) {
navigate(course.get(index));
}
}
/**
* Add issue to given block.
*
* @param block block to add a new issue to
*/
public void addIssue(Block block) {
if (Objects.nonNull(block)) {
block.addIssue();
block.checkIssuesWithSameHeading();
}
}
/**
* Get the color from the list of defined colors for the given index.
* These colors are used to highlight and distinguish the different issues in the calendar.
*
* @param index index to retrieve color for from list of colors
* @return The color represented by a String containing the color's hex value
*/
public String getIssueColor(int index) {
if (index >= 0 && index < issueColours.length) {
return issueColours[index];
}
return "";
}
/**
* The method will be called by Faces to store the new
* value of the read-write property "uploadedFile", which is a reference to
* the binary data the user provides for upload.
*
* @param data
* the UploadedFile object generated by the Tomahawk library
*/
public void setUploadedFile(UploadedFile data) {
uploadedFile = data;
}
/**
* Upload an XML file to import a course of appearance.
* Overrides the existing contents of course with the contents
* of the XML file.
*/
public void upload() {
try {
if (Objects.isNull(uploadedFile)) {
Helper.setMessage(UPLOAD_ERROR, "calendar.upload.isEmpty");
return;
}
Document xml = XMLUtils.load(uploadedFile.getInputStream());
course = new Course(xml);
Helper.removeManagedBean("GranularityForm");
navigate(course.get(0));
} catch (SAXException e) {
Helper.setErrorMessage(UPLOAD_ERROR, "errorSAXException", logger, e);
} catch (IOException e) {
Helper.setErrorMessage(UPLOAD_ERROR, e.getLocalizedMessage(), logger, e);
} catch (IllegalArgumentException e) {
Helper.setErrorMessage("calendar.upload.overlappingDateRanges", logger, e);
} catch (NoSuchElementException e) {
Helper.setErrorMessage(UPLOAD_ERROR, "calendar.upload.missingMandatoryElement", logger, e);
} catch (NullPointerException e) {
Helper.setErrorMessage("calendar.upload.missingMandatoryValue", logger, e);
} finally {
uploadedFile = null;
}
}
/**
* Create processes for the modelled course of appearance and chosen granularity.
*/
public String createProcesses() throws DAOException {
Process process = ServiceManager.getProcessService().getById(parentId);
TaskManager.addTask(new GeneratesNewspaperProcessesThread(process, course));
if (ServiceManager.getSecurityAccessService().hasAuthorityToViewTaskManagerPage()) {
return TASK_MANAGER_REFERER;
}
return DEFAULT_REFERER;
}
public String formatString(String messageKey, String... replacements) {
return Helper.getTranslation(messageKey, replacements);
}
/**
* Get the first day of the year.
* This might differ from January 1st as business years might have a different range of time.
* The used PrimeFaces component requires a Date object including a specific year,
* however the year is irrelevant for yearStart itself.
*
* @return Date representing the first day of the year
*/
public Date getYearStart() {
Calendar calendar = new GregorianCalendar(
today.getYear(), course.getYearStart().getMonth().ordinal(), course.getYearStart().getDayOfMonth());
return calendar.getTime();
}
/**
* Set the first day of the year.
* This might differ from January 1st as business years might have a different range of time.
* The used PrimeFaces component passes a Date object including a specific year,
* however the year is irrelevant for yearStart itself.
*
* @param date Date representing the first day of the year
*/
public void setYearStart(Date date) {
if (Objects.nonNull(date)) {
course.setYearStart(MonthDay.of(date.getMonth() + 1, date.getDate()));
}
}
/**
* Returns the name of the year. The name of the year is optional and maybe
* empty. Typical values are “Business year”, “Fiscal year”, or “Season”.
*
* @return the name of the year
*/
public String getYearName() {
return course.getYearName();
}
/**
* Sets the year name of the course.
*
* @param yearName
* the yearName to set
*/
public void setYearName(String yearName) {
course.setYearName(yearName);
}
/**
* Get today.
*
* @return value of today
*/
public LocalDate getToday() {
return today;
}
/**
* Add new metadata for the selected Block and with the selected date and Issue.
*/
public void addMetadata(Issue issue, boolean onlyThisIssue) {
IndividualIssue selectedIssue = null;
for (IndividualIssue individualIssue : getIndividualIssues(selectedBlock)) {
if (Objects.nonNull(issue)
&& Objects.equals(individualIssue.getIssue(), issue)
&& Objects.equals(selectedDate, individualIssue.getDate())) {
selectedIssue = individualIssue;
break;
}
}
if (!selectedBlock.getIssues().isEmpty() && Objects.nonNull(selectedIssue)) {
CountableMetadata metadata = new CountableMetadata(selectedBlock,
Triple.of(selectedIssue.getDate(), selectedIssue.getIssue(), onlyThisIssue));
if (!metadata.getAllMetadataTypes().isEmpty()) {
metadata.setMetadataDetail(metadata.getAllMetadataTypes().get(0));
}
selectedBlock.addMetadata(metadata);
} else {
Helper.setErrorMessage("Selected issue or list of issues must not be empty for selectedBlock: " + selectedBlock.toString());
}
}
/**
* Get selectedDate.
*
* @return value of selectedDate
*/
public LocalDate getSelectedDate() {
return selectedDate;
}
/**
* Set selectedDate.
*
* @param selectedDate as java.time.LocalDate
*/
public void setSelectedDate(LocalDate selectedDate) {
this.selectedDate = selectedDate;
}
/**
* Get selectedBlock.
*
* @return value of selectedBlock
*/
public Block getSelectedBlock() {
return selectedBlock;
}
/**
* Set the selected block based on the selected date.
*/
public void setSelectedBlock() {
if (Objects.nonNull(selectedDate)) {
for (Block block : course) {
if ((block.getFirstAppearance().isBefore(selectedDate) || block.getFirstAppearance().isEqual(selectedDate))
&& (block.getLastAppearance().isAfter(selectedDate) || block.getLastAppearance().isEqual(selectedDate))) {
selectedBlock = block;
break;
}
}
}
}
/**
* Set selectedBlock.
*
* @param selectedBlock as org.kitodo.production.model.bibliography.course.Block
*/
public void setSelectedBlock(Block selectedBlock) {
this.selectedBlock = selectedBlock;
}
/**
* Return a list of all individual issues for this block.
*
* @return the list of issues
*/
public List<IndividualIssue> getIndividualIssues(Block block) {
return CalendarService.getIndividualIssues(block);
}
/**
* Get list of metadata for given block on a specific date and issue.
*
* @param block the block to get the metadata from
* @param date the date to get the metadata for
* @param issue the issue to get the metadata for
* @return list of matching metadata
*/
public List<CountableMetadata> getMetadata(Block block, LocalDate date, Issue issue) {
return CalendarService.getMetadata(block, date, issue);
}
/**
* Set the issue and date where the given metadata occurred last.
*
* @param metadata the metadata to set the end date and issue for
* @param date date where the metadata occurred last
* @param issue issue where the metadata occurred last
*/
public void setLastIssue(CountableMetadata metadata, LocalDate date, Issue issue) {
if (Objects.nonNull(metadata)) {
metadata.setDelete(new ImmutablePair<>(date, issue));
}
}
/**
* Get value of countable metadata for the given date and issue.
*
* @param metadata the metadata to calculate the the value for
* @param date the date to calculate the value for
* @param issue the issue to calculate the metadata for
* @return the metadata value as java.lang.String
*/
public String getTextMetadataValue(CountableMetadata metadata, LocalDate date, Issue issue) {
if (Objects.nonNull(metadata) && metadata.getMetadataDetail() instanceof ProcessTextMetadata) {
return metadata.getValue(new ImmutablePair<>(date, issue), course.getYearStart());
}
return "";
}
/**
* Get all metadata of the given block as summary.
* Each type of metadata is only listed once with its earliest occurrence.
*
* @param block the block to get the metadata for
* @return list of pairs containing the metadata type and the date of its earliest occurrence
*/
public List<Pair<ProcessDetail, LocalDate>> getMetadataSummary(Block block) {
return CalendarService.getMetadataSummary(block);
}
/**
* Get the value of the given processDetail.
*
* @param processDetail
* as ProcessDetail
* @return the value as a java.lang.String
*/
public String getMetadataValue(ProcessDetail processDetail) {
return ImportService.getProcessDetailValue(processDetail);
}
/**
* Check if process with the same processtitle already exists.
*/
public void checkDuplicatedTitles() throws ProcessGenerationException, DataException, DAOException,
ConfigurationException, IOException, DoctypeMissingException {
if (course.parallelStream().noneMatch(block -> Objects.equals(block.checkIssuesWithSameHeading(), true))) {
Process process = ServiceManager.getProcessService().getById(parentId);
NewspaperProcessesGenerator newspaperProcessesGenerator = new NewspaperProcessesGenerator(process, course);
newspaperProcessesGenerator.initialize();
if (!newspaperProcessesGenerator.isDuplicatedTitles()) {
PrimeFaces.current().executeScript("PF('createProcessesConfirmDialog').show();");
}
}
}
/**
* Get first issue that's appear on the selected Date.
* @return issue
*/
public Issue getFirstMatchIssue() {
if (selectedDate != null) {
return getCalendarSheet().get(selectedDate.getDayOfMonth() - 1).get(selectedDate.getMonthValue() - 1).getIssues()
.parallelStream()
.filter(issue -> issue.isMatch(selectedDate))
.findFirst().orElse(null);
}
return null;
}
/**
* add Metadata to all Issues that's appear on the selected Date.
*/
public void addMetadataToAllMatchIssues() {
if (getFirstMatchIssue() != null) {
addMetadata(getFirstMatchIssue(), false);
}
}
}