/** * 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.List; import java.util.Map; import java.util.HashMap; import java.util.Iterator; import org.apache.log4j.Logger; import org.dspace.content.Collection; import org.dspace.content.MetadataValue; import org.dspace.content.authority.service.ChoiceAuthorityService; import org.dspace.core.service.PluginService; import org.dspace.services.ConfigurationService; 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.contributer.author") * * {@code * # names the ChoiceAuthority plugin called for this field * choices.plugin.<FIELD> = 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.<FIELD> = "select" | "suggest" * * # is value "closed" to the set of these choices or are non-authority values permitted? * choices.closed.<FIELD> = true | false * } * @author Larry Stone * @see ChoiceAuthority */ public final class ChoiceAuthorityServiceImpl implements ChoiceAuthorityService { private Logger log = Logger.getLogger(ChoiceAuthorityServiceImpl.class); // map of field key to authority plugin protected Map<String,ChoiceAuthority> controller = new HashMap<String,ChoiceAuthority>(); // map of field key to presentation type protected Map<String,String> presentation = new HashMap<String,String>(); // map of field key to closed value protected Map<String,Boolean> closed = new HashMap<String,Boolean>(); @Autowired(required = true) protected ConfigurationService configurationService; @Autowired(required = true) protected PluginService pluginService; private final String CHOICES_PLUGIN_PREFIX = "choices.plugin."; private final String CHOICES_PRESENTATION_PREFIX = "choices.presentation."; private final 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 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 = getChoiceAuthorityMap().get(fieldKey); if (ma == null) { throw new IllegalArgumentException( "No choices plugin was configured for field \"" + fieldKey + "\"."); } return ma.getMatches(fieldKey, query, collection, start, limit, locale); } @Override public Choices getMatches(String fieldKey, String query, Collection collection, int start, int limit, String locale, boolean externalInput) { ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey); if (ma == null) { throw new IllegalArgumentException( "No choices plugin was configured for field \"" + fieldKey + "\"."); } if (externalInput && ma instanceof SolrAuthority) { ((SolrAuthority)ma).addExternalResultsInNextMatches(); } return ma.getMatches(fieldKey, query, collection, start, limit, locale); } @Override public Choices getBestMatch(String fieldKey, String query, Collection collection, String locale) { ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey); if (ma == null) { throw new IllegalArgumentException( "No choices plugin was configured for field \"" + fieldKey + "\"."); } return ma.getBestMatch(fieldKey, query, collection, locale); } @Override public String getLabel(MetadataValue metadataValue, String locale) { return getLabel(metadataValue.getMetadataField().toString(), metadataValue.getAuthority(), locale); } @Override public String getLabel(String fieldKey, String authKey, String locale) { ChoiceAuthority ma = getChoiceAuthorityMap().get(fieldKey); if (ma == null) { throw new IllegalArgumentException("No choices plugin was configured for field \"" + fieldKey + "\"."); } return ma.getLabel(fieldKey, authKey, locale); } @Override public boolean isChoicesConfigured(String fieldKey) { return getChoiceAuthorityMap().containsKey(fieldKey); } @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<String> getVariants(MetadataValue metadataValue) { ChoiceAuthority ma = getChoiceAuthorityMap().get(metadataValue.getMetadataField().toString()); if (ma instanceof AuthorityVariantsSupport) { AuthorityVariantsSupport avs = (AuthorityVariantsSupport) ma; return avs.getVariants(metadataValue.getAuthority(), metadataValue.getLanguage()); } return null; } protected String makeFieldKey(String schema, String element, String qualifier) { if (qualifier == null) { return schema + "_" + element; } else { return schema + "_" + element + "_" + qualifier; } } /** * Return map of key to ChoiceAuthority plugin * @return */ private Map<String,ChoiceAuthority> getChoiceAuthorityMap() { // If empty, load from configuration if(controller.isEmpty()) { // Get all configuration keys starting with a given prefix List<String> propKeys = configurationService.getPropertyKeys(CHOICES_PLUGIN_PREFIX); Iterator<String> 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. ChoiceAuthority ma = (ChoiceAuthority) pluginService.getNamedPlugin(ChoiceAuthority.class, configurationService.getProperty(key)); if (ma == null) { log.warn("Skipping invalid configuration for "+key+" because named plugin not found: "+configurationService.getProperty(key)); continue; } controller.put(fkey, ma); log.debug("Choice Control: For field="+fkey+", Plugin="+ma); } } return controller; } /** * Return map of key to presentation * @return */ private Map<String,String> getPresentationMap() { // If empty, load from configuration if(presentation.isEmpty()) { // Get all configuration keys starting with a given prefix List<String> propKeys = configurationService.getPropertyKeys(CHOICES_PRESENTATION_PREFIX); Iterator<String> 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<String,Boolean> getClosedMap() { // If empty, load from configuration if(closed.isEmpty()) { // Get all configuration keys starting with a given prefix List<String> propKeys = configurationService.getPropertyKeys(CHOICES_CLOSED_PREFIX); Iterator<String> 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; } }