
org.dspace.content.authority.ChoiceAuthorityServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
The newest version!
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content.authority;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.dspace.app.util.DCInput;
import org.dspace.app.util.DCInputSet;
import org.dspace.app.util.DCInputsReader;
import org.dspace.app.util.DCInputsReaderException;
import org.dspace.app.util.SubmissionConfig;
import org.dspace.app.util.SubmissionConfigReaderException;
import org.dspace.content.Collection;
import org.dspace.content.MetadataValue;
import org.dspace.content.authority.service.ChoiceAuthorityService;
import org.dspace.core.Utils;
import org.dspace.core.service.PluginService;
import org.dspace.discovery.configuration.DiscoveryConfigurationService;
import org.dspace.discovery.configuration.DiscoverySearchFilterFacet;
import org.dspace.services.ConfigurationService;
import org.dspace.submit.factory.SubmissionServiceFactory;
import org.dspace.submit.service.SubmissionConfigService;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Broker for ChoiceAuthority plugins, and for other information configured
* about the choice aspect of authority control for a metadata field.
*
* Configuration keys, per metadata field (e.g. "dc.contributor.author")
*
* {@code
* # names the ChoiceAuthority plugin called for this field
* choices.plugin. = name-of-plugin
*
* # mode of UI presentation desired in submission UI:
* # "select" is dropdown menu, "lookup" is popup with selector, "suggest" is autocomplete/suggest
* choices.presentation. = "select" | "suggest"
*
* # is value "closed" to the set of these choices or are non-authority values permitted?
* choices.closed. = true | false
* }
*
* @author Larry Stone
* @see ChoiceAuthority
*/
public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService {
private Logger log = org.apache.logging.log4j.LogManager.getLogger(ChoiceAuthorityServiceImpl.class);
// map of field key to authority plugin
protected Map controller = new HashMap();
// map of field key, form definition to authority plugin
protected Map> controllerFormDefinitions =
new HashMap>();
// map of field key to presentation type
protected Map presentation = new HashMap();
// map of field key to closed value
protected Map closed = new HashMap();
// flag to track the initialization status of the service
private boolean initialized = false;
// map of authority name to field keys (the same authority can be configured over multiple metadata)
protected Map> authorities = new HashMap>();
// map of authority name to form definition and field keys
protected Map>> authoritiesFormDefinitions =
new HashMap>>();
// Map of vocabulary authorities to and their index info equivalent
protected Map vocabularyIndexMap = new HashMap<>();
// the item submission reader
private SubmissionConfigService submissionConfigService;
@Autowired(required = true)
protected ConfigurationService configurationService;
@Autowired(required = true)
protected PluginService pluginService;
@Autowired
private DiscoveryConfigurationService searchConfigurationService;
final static String CHOICES_PLUGIN_PREFIX = "choices.plugin.";
final static String CHOICES_PRESENTATION_PREFIX = "choices.presentation.";
final static String CHOICES_CLOSED_PREFIX = "choices.closed.";
protected ChoiceAuthorityServiceImpl() {
}
// translate tail of configuration key (supposed to be schema.element.qual)
// into field key
protected String config2fkey(String field) {
// field is expected to be "schema.element.qualifier"
int dot = field.indexOf('.');
if (dot < 0) {
return null;
}
String schema = field.substring(0, dot);
String element = field.substring(dot + 1);
String qualifier = null;
dot = element.indexOf('.');
if (dot >= 0) {
qualifier = element.substring(dot + 1);
element = element.substring(0, dot);
}
return makeFieldKey(schema, element, qualifier);
}
@Override
public Set getChoiceAuthoritiesNames() {
init();
Set authoritiesNames = new HashSet();
authoritiesNames.addAll(authorities.keySet());
authoritiesNames.addAll(authoritiesFormDefinitions.keySet());
return authoritiesNames;
}
private synchronized void init() {
if (!initialized) {
try {
submissionConfigService = SubmissionServiceFactory.getInstance().getSubmissionConfigService();
} catch (SubmissionConfigReaderException e) {
// the system is in an illegal state as the submission definition is not valid
throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
e);
}
loadChoiceAuthorityConfigurations();
initialized = true;
}
}
@Override
public Choices getMatches(String schema, String element, String qualifier,
String query, Collection collection, int start, int limit, String locale) {
return getMatches(makeFieldKey(schema, element, qualifier), query,
collection, start, limit, locale);
}
@Override
public Choices getMatches(String fieldKey, String query, Collection collection,
int start, int limit, String locale) {
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
if (ma == null) {
throw new IllegalArgumentException(
"No choices plugin was configured for field \"" + fieldKey
+ "\", collection=" + collection.getID().toString() + ".");
}
return ma.getMatches(query, start, limit, locale);
}
@Override
public Choices getBestMatch(String fieldKey, String query, Collection collection,
String locale) {
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
if (ma == null) {
throw new IllegalArgumentException(
"No choices plugin was configured for field \"" + fieldKey
+ "\", collection=" + collection.getID().toString() + ".");
}
return ma.getBestMatch(query, locale);
}
@Override
public String getLabel(MetadataValue metadataValue, Collection collection, String locale) {
return getLabel(metadataValue.getMetadataField().toString(), collection, metadataValue.getAuthority(), locale);
}
@Override
public String getLabel(String fieldKey, Collection collection, String authKey, String locale) {
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
if (ma == null) {
throw new IllegalArgumentException(
"No choices plugin was configured for field \"" + fieldKey
+ "\", collection=" + collection.getID().toString() + ".");
}
return ma.getLabel(authKey, locale);
}
@Override
public boolean isChoicesConfigured(String fieldKey, Collection collection) {
return getAuthorityByFieldKeyCollection(fieldKey, collection) != null;
}
@Override
public String getPresentation(String fieldKey) {
return getPresentationMap().get(fieldKey);
}
@Override
public boolean isClosed(String fieldKey) {
return getClosedMap().containsKey(fieldKey) && getClosedMap().get(fieldKey);
}
@Override
public List getVariants(MetadataValue metadataValue, Collection collection) {
String fieldKey = metadataValue.getMetadataField().toString();
ChoiceAuthority ma = getAuthorityByFieldKeyCollection(fieldKey, collection);
if (ma == null) {
throw new IllegalArgumentException(
"No choices plugin was configured for field \"" + fieldKey
+ "\", collection=" + collection.getID().toString() + ".");
}
if (ma instanceof AuthorityVariantsSupport) {
AuthorityVariantsSupport avs = (AuthorityVariantsSupport) ma;
return avs.getVariants(metadataValue.getAuthority(), metadataValue.getLanguage());
}
return null;
}
@Override
public String getChoiceAuthorityName(String schema, String element, String qualifier, Collection collection) {
init();
String fieldKey = makeFieldKey(schema, element, qualifier);
// check if there is an authority configured for the metadata valid for all the collections
if (controller.containsKey(fieldKey)) {
for (Entry> authority2md : authorities.entrySet()) {
if (authority2md.getValue().contains(fieldKey)) {
return authority2md.getKey();
}
}
} else if (collection != null && controllerFormDefinitions.containsKey(fieldKey)) {
// there is an authority configured for the metadata valid for some collections,
// check if it is the requested collection
Map controllerFormDef = controllerFormDefinitions.get(fieldKey);
SubmissionConfig submissionConfig = submissionConfigService
.getSubmissionConfigByCollection(collection);
String submissionName = submissionConfig.getSubmissionName();
// check if the requested collection has a submission definition that use an authority for the metadata
if (controllerFormDef.containsKey(submissionName)) {
for (Entry>> authority2defs2md :
authoritiesFormDefinitions.entrySet()) {
List mdByDefinition = authority2defs2md.getValue().get(submissionName);
if (mdByDefinition != null && mdByDefinition.contains(fieldKey)) {
return authority2defs2md.getKey();
}
}
}
}
return null;
}
protected String makeFieldKey(String schema, String element, String qualifier) {
return Utils.standardize(schema, element, qualifier, "_");
}
@Override
public void clearCache() throws SubmissionConfigReaderException {
controller.clear();
authorities.clear();
presentation.clear();
closed.clear();
controllerFormDefinitions.clear();
authoritiesFormDefinitions.clear();
submissionConfigService.reload();
initialized = false;
}
private void loadChoiceAuthorityConfigurations() {
// Get all configuration keys starting with a given prefix
List propKeys = configurationService.getPropertyKeys(CHOICES_PLUGIN_PREFIX);
Iterator keyIterator = propKeys.iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
String fkey = config2fkey(key.substring(CHOICES_PLUGIN_PREFIX.length()));
if (fkey == null) {
log.warn(
"Skipping invalid ChoiceAuthority configuration property: " + key + ": does not have schema" +
".element.qualifier");
continue;
}
// XXX FIXME maybe add sanity check, call
// MetadataField.findByElement to make sure it's a real field.
String authorityName = configurationService.getProperty(key);
ChoiceAuthority ma = (ChoiceAuthority)
pluginService.getNamedPlugin(ChoiceAuthority.class, authorityName);
if (ma == null) {
log.warn(
"Skipping invalid configuration for " + key + " because named plugin not found: " + authorityName);
continue;
}
controller.put(fkey, ma);
List fkeys;
if (authorities.containsKey(authorityName)) {
fkeys = authorities.get(authorityName);
} else {
fkeys = new ArrayList();
}
fkeys.add(fkey);
authorities.put(authorityName, fkeys);
log.debug("Choice Control: For field=" + fkey + ", Plugin=" + ma);
}
autoRegisterChoiceAuthorityFromInputReader();
}
/**
* This method will register all the authorities that are required due to the
* submission forms configuration. This includes authorities for value pairs and
* xml vocabularies
*/
private void autoRegisterChoiceAuthorityFromInputReader() {
try {
List submissionConfigs = submissionConfigService
.getAllSubmissionConfigs(Integer.MAX_VALUE, 0);
DCInputsReader dcInputsReader = new DCInputsReader();
// loop over all the defined item submission configuration
for (SubmissionConfig subCfg : submissionConfigs) {
String submissionName = subCfg.getSubmissionName();
List inputsBySubmissionName = dcInputsReader.getInputsBySubmissionName(submissionName);
// loop over the submission forms configuration eventually associated with the submission panel
for (DCInputSet dcinputSet : inputsBySubmissionName) {
DCInput[][] dcinputs = dcinputSet.getFields();
for (DCInput[] dcrows : dcinputs) {
for (DCInput dcinput : dcrows) {
// for each input in the form check if it is associated with a real value pairs
// or an xml vocabulary
String authorityName = null;
if (StringUtils.isNotBlank(dcinput.getPairsType())
&& !StringUtils.equals(dcinput.getInputType(), "qualdrop_value")) {
authorityName = dcinput.getPairsType();
} else if (StringUtils.isNotBlank(dcinput.getVocabulary())) {
authorityName = dcinput.getVocabulary();
}
// do we have an authority?
if (StringUtils.isNotBlank(authorityName)) {
String fieldKey = makeFieldKey(dcinput.getSchema(), dcinput.getElement(),
dcinput.getQualifier());
ChoiceAuthority ca = controller.get(authorityName);
if (ca == null) {
ca = (ChoiceAuthority) pluginService
.getNamedPlugin(ChoiceAuthority.class, authorityName);
if (ca == null) {
throw new IllegalStateException("Invalid configuration for " + fieldKey
+ " in submission definition " + submissionName
+ ", form definition " + dcinputSet.getFormName()
+ " no named plugin found: " + authorityName);
}
}
addAuthorityToFormCacheMap(submissionName, fieldKey, ca);
addFormDetailsToAuthorityCacheMap(submissionName, authorityName, fieldKey);
}
}
}
}
}
} catch (DCInputsReaderException e) {
// the system is in an illegal state as the submission definition is not valid
throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
e);
}
}
/**
* Add the form/field to the cache map keeping track of which form/field are
* associated with the specific authority name
*
* @param submissionName the form definition name
* @param authorityName the name of the authority plugin
* @param fieldKey the field key that use the authority
*/
private void addFormDetailsToAuthorityCacheMap(String submissionName, String authorityName, String fieldKey) {
Map> submissionDefinitionNames2fieldKeys;
if (authoritiesFormDefinitions.containsKey(authorityName)) {
submissionDefinitionNames2fieldKeys = authoritiesFormDefinitions.get(authorityName);
} else {
submissionDefinitionNames2fieldKeys = new HashMap>();
}
List fields;
if (submissionDefinitionNames2fieldKeys.containsKey(submissionName)) {
fields = submissionDefinitionNames2fieldKeys.get(submissionName);
} else {
fields = new ArrayList();
}
fields.add(fieldKey);
submissionDefinitionNames2fieldKeys.put(submissionName, fields);
authoritiesFormDefinitions.put(authorityName, submissionDefinitionNames2fieldKeys);
}
/**
* Add the authority plugin to the cache map keeping track of which authority is
* used by a specific form/field
*
* @param submissionName the submission definition name
* @param fieldKey the field key that require the authority
* @param ca the authority plugin
*/
private void addAuthorityToFormCacheMap(String submissionName, String fieldKey, ChoiceAuthority ca) {
Map definition2authority;
if (controllerFormDefinitions.containsKey(fieldKey)) {
definition2authority = controllerFormDefinitions.get(fieldKey);
} else {
definition2authority = new HashMap();
}
definition2authority.put(submissionName, ca);
controllerFormDefinitions.put(fieldKey, definition2authority);
}
/**
* Return map of key to presentation
*
* @return
*/
private Map getPresentationMap() {
// If empty, load from configuration
if (presentation.isEmpty()) {
// Get all configuration keys starting with a given prefix
List propKeys = configurationService.getPropertyKeys(CHOICES_PRESENTATION_PREFIX);
Iterator keyIterator = propKeys.iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
String fkey = config2fkey(key.substring(CHOICES_PRESENTATION_PREFIX.length()));
if (fkey == null) {
log.warn(
"Skipping invalid ChoiceAuthority configuration property: " + key + ": does not have schema" +
".element.qualifier");
continue;
}
presentation.put(fkey, configurationService.getProperty(key));
}
}
return presentation;
}
/**
* Return map of key to closed setting
*
* @return
*/
private Map getClosedMap() {
// If empty, load from configuration
if (closed.isEmpty()) {
// Get all configuration keys starting with a given prefix
List propKeys = configurationService.getPropertyKeys(CHOICES_CLOSED_PREFIX);
Iterator keyIterator = propKeys.iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
String fkey = config2fkey(key.substring(CHOICES_CLOSED_PREFIX.length()));
if (fkey == null) {
log.warn(
"Skipping invalid ChoiceAuthority configuration property: " + key + ": does not have schema" +
".element.qualifier");
continue;
}
closed.put(fkey, configurationService.getBooleanProperty(key));
}
}
return closed;
}
@Override
public ChoiceAuthority getChoiceAuthorityByAuthorityName(String authorityName) {
ChoiceAuthority ma = (ChoiceAuthority)
pluginService.getNamedPlugin(ChoiceAuthority.class, authorityName);
if (ma == null) {
throw new IllegalArgumentException(
"No choices plugin was configured for authorityName \"" + authorityName
+ "\".");
}
return ma;
}
private ChoiceAuthority getAuthorityByFieldKeyCollection(String fieldKey, Collection collection) {
init();
ChoiceAuthority ma = controller.get(fieldKey);
if (ma == null && collection != null) {
SubmissionConfigService configReaderService;
try {
configReaderService = SubmissionServiceFactory.getInstance().getSubmissionConfigService();
SubmissionConfig submissionName = configReaderService
.getSubmissionConfigByCollection(collection);
ma = controllerFormDefinitions.get(fieldKey).get(submissionName.getSubmissionName());
} catch (SubmissionConfigReaderException e) {
// the system is in an illegal state as the submission definition is not valid
throw new IllegalStateException("Error reading the item submission configuration: " + e.getMessage(),
e);
}
}
return ma;
}
@Override
public boolean storeAuthority(String fieldKey, Collection collection) {
// currently only named authority can eventually provide real authority
return controller.containsKey(fieldKey);
}
/**
* Wrapper that calls getChoicesByParent method of the plugin.
*
* @param authorityName authority name
* @param parentId parent Id
* @param start choice at which to start, 0 is first.
* @param limit maximum number of choices to return, 0 for no limit.
* @param locale explicit localization key if available, or null
* @return a Choices object (never null).
* @see org.dspace.content.authority.ChoiceAuthority#getChoicesByParent(java.lang.String, java.lang.String,
* int, int, java.lang.String)
*/
@Override
public Choices getChoicesByParent(String authorityName, String parentId, int start, int limit, String locale) {
HierarchicalAuthority ma = (HierarchicalAuthority) getChoiceAuthorityByAuthorityName(authorityName);
return ma.getChoicesByParent(authorityName, parentId, start, limit, locale);
}
/**
* Wrapper that calls getTopChoices method of the plugin.
*
* @param authorityName authority name
* @param start choice at which to start, 0 is first.
* @param limit maximum number of choices to return, 0 for no limit.
* @param locale explicit localization key if available, or null
* @return a Choices object (never null).
* @see org.dspace.content.authority.ChoiceAuthority#getTopChoices(java.lang.String, int, int, java.lang.String)
*/
@Override
public Choices getTopChoices(String authorityName, int start, int limit, String locale) {
HierarchicalAuthority ma = (HierarchicalAuthority) getChoiceAuthorityByAuthorityName(authorityName);
return ma.getTopChoices(authorityName, start, limit, locale);
}
@Override
public Choice getParentChoice(String authorityName, String vocabularyId, String locale) {
HierarchicalAuthority ma = (HierarchicalAuthority) getChoiceAuthorityByAuthorityName(authorityName);
return ma.getParentChoice(authorityName, vocabularyId, locale);
}
@Override
public DSpaceControlledVocabularyIndex getVocabularyIndex(String nameVocab) {
if (this.vocabularyIndexMap.containsKey(nameVocab)) {
return this.vocabularyIndexMap.get(nameVocab);
} else {
init();
ChoiceAuthority source = this.getChoiceAuthorityByAuthorityName(nameVocab);
if (source != null && source instanceof DSpaceControlledVocabulary) {
// First, check if this vocabulary index is disabled
String[] vocabulariesDisabled = configurationService
.getArrayProperty("webui.browse.vocabularies.disabled");
if (vocabulariesDisabled != null && ArrayUtils.contains(vocabulariesDisabled, nameVocab)) {
// Discard this vocabulary browse index
return null;
}
Set metadataFields = new HashSet<>();
Map> formsToFields = this.authoritiesFormDefinitions.get(nameVocab);
for (Map.Entry> formToField : formsToFields.entrySet()) {
metadataFields.addAll(formToField.getValue().stream().map(value ->
StringUtils.replace(value, "_", "."))
.collect(Collectors.toList()));
}
DiscoverySearchFilterFacet matchingFacet = null;
for (DiscoverySearchFilterFacet facetConfig : searchConfigurationService.getAllFacetsConfig()) {
boolean coversAllFieldsFromVocab = true;
for (String fieldFromVocab: metadataFields) {
boolean coversFieldFromVocab = false;
for (String facetMdField: facetConfig.getMetadataFields()) {
if (facetMdField.startsWith(fieldFromVocab)) {
coversFieldFromVocab = true;
break;
}
}
if (!coversFieldFromVocab) {
coversAllFieldsFromVocab = false;
break;
}
}
if (coversAllFieldsFromVocab) {
matchingFacet = facetConfig;
break;
}
}
// If there is no matching facet, return null to ignore this vocabulary index
if (matchingFacet == null) {
return null;
}
DSpaceControlledVocabularyIndex vocabularyIndex =
new DSpaceControlledVocabularyIndex((DSpaceControlledVocabulary) source, metadataFields,
matchingFacet);
this.vocabularyIndexMap.put(nameVocab, vocabularyIndex);
return vocabularyIndex;
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy