Coverage Summary for Class: Labeled (org.kitodo.dataeditor.ruleset)
Class |
Class, %
|
Method, %
|
Line, %
|
Labeled |
100%
(1/1)
|
100%
(8/8)
|
100%
(42/42)
|
/*
* (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.dataeditor.ruleset;
import java.text.Collator;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Locale.LanguageRange;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.function.Function;
import org.apache.commons.lang3.tuple.Pair;
import org.kitodo.dataeditor.ruleset.xml.Label;
import org.kitodo.dataeditor.ruleset.xml.Ruleset;
/**
* A provider for translated labels for elements. There are three of these, as
* divisions, keys and options.
*/
public class Labeled {
/**
* The undefined language. Yes, there is something like that. But it isn’t
* spoken by anyone.
*/
private static final String UNDEFINED_LANGUAGE = "und";
/**
* The undefined language range. This is an area where only languages that
* do not exist are spoken, so basically none are spoken.
*/
private static final List<LanguageRange> UNDEFINED_LANGUAGE_RANGE = LanguageRange.parse(UNDEFINED_LANGUAGE);
/**
* The undefined locale. A place where the unspoken language is spoken. So
* nowhere.
*/
private static final Locale UNDEFINED_LOCALE = Locale.forLanguageTag(UNDEFINED_LANGUAGE);
/**
* Lists the entries alphabetically by the translated label, taking into
* account the language preferred by the user for alphabetical sorting. In
* the auxiliary map, the keys are sorted by the sequence translated label +
* separator + key, to account for the case where multiple keys are
* (inadvertently) translated equally.
*
* @param ruleset
* The ruleset. From the ruleset, we learn which language is a
* label that does not have a language attribute, that is what is
* the default language of the rule set.
* @param elements
* The items to sort. The function is so generic that you can
* sort divisions, keys, and options with it.
* @param keyGetter
* A function to extract from the element the value used in the
* result map as key.
* @param labelGetter
* A function that allows you to extract the list of labels from
* the element and then select the best translated one.
* @param priorityList
* The list of languages spoken by the user human
* @return a map in the order of the best-fitting translated label
*/
public static <T> LinkedHashMap<String, String> listByTranslatedLabel(Ruleset ruleset, Collection<T> elements,
Function<T, String> keyGetter, Function<T, Collection<Label>> labelGetter,
List<LanguageRange> priorityList) {
Locale sortLocale = Locale.lookup(priorityList, Arrays.asList(Collator.getAvailableLocales()));
TreeMap<String, Pair<String, String>> byLabelSorter = sortLocale != null
? new TreeMap<>(Collator.getInstance(sortLocale))
: new TreeMap<>();
for (T element : elements) {
String key = keyGetter.apply(element);
Labeled labeled = new Labeled(ruleset, key, labelGetter.apply(element));
String label = labeled.getLabel(priorityList);
byLabelSorter.put(label + '\037' + key, Pair.of(key, label));
}
LinkedHashMap<String, String> list = new LinkedHashMap<>((int) Math.ceil(elements.size() / 0.75));
for (Pair<String, String> entry : byLabelSorter.values()) {
list.put(entry.getKey(), entry.getValue());
}
return list;
}
/**
* The identifier serves to identify itself.
*/
protected final String id;
/**
* Label everywhere.
*/
private final Collection<Label> labels;
/**
* The ruleset.
*/
protected final Ruleset ruleset;
/**
* That may be whether he is undefined or not.
*/
private final Boolean undefined;
/**
* Constructor for a provider for translated labels.
*
* @param ruleset
* the ruleset
* @param id
* the identifier
* @param labels
* the labels
*/
Labeled(Ruleset ruleset, String id, Collection<Label> labels) {
this.ruleset = ruleset;
this.id = id;
this.labels = labels;
this.undefined = null;
}
/**
* Constructor for a provider for translated labels. This is protected and
* called only by subclasses, as part of them. Every key declaration, also
* nesting keys and divisions are labeled.
*
* @param ruleset
* the ruleset
* @param id
* the identifier
* @param labels
* the labels
* @param undefined
* whether he is undefined or not
*/
protected Labeled(Ruleset ruleset, String id, Collection<Label> labels, boolean undefined) {
this.ruleset = ruleset;
this.id = id;
this.labels = labels;
this.undefined = undefined;
}
/**
* Returns the identifier.
*
* @return the identifier
*/
public String getId() {
return id;
}
/**
* Returns the etiquette in the favorite language.
*
* @param priorityList
* wish list for favorite language
* @return the etiquette in the favorite language
*/
String getLabel(List<LanguageRange> priorityList) {
Map<Locale, String> labelMap = new HashMap<>();
for (Label label : labels) {
Optional<Locale> langAttribute = label.getLanguage();
if (langAttribute.isPresent()) {
labelMap.put(langAttribute.get(), label.getValue());
} else {
labelMap.put(new Locale(ruleset.getDefaultLang()), label.getValue());
labelMap.put(UNDEFINED_LOCALE, label.getValue());
}
}
Locale bestMatching = Locale.lookup(priorityList, labelMap.keySet());
if (bestMatching == null) {
bestMatching = Locale.lookup(UNDEFINED_LANGUAGE_RANGE, labelMap.keySet());
}
return Optional.ofNullable(labelMap.get(bestMatching)).orElse(id);
}
/**
* Access method for the labels (to pass them to the
* {@code listByTranslatedLabel()} method).
*
* @return the labels
*/
public Collection<Label> getLabels() {
return labels;
}
/**
* Returns whether he is undefined or not.
*
* @return whether he is undefined or not
*/
boolean isUndefined() {
return undefined;
}
}