/*
* ChoiceAuthorityManager.java
*
* Version: $Revision: 3705 $
*
* Date: $Date: 2009-04-11 13:02:24 -0400 (Sat, 11 Apr 2009) $
*
* Copyright (c) 2002-2009, The DSpace Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the DSpace Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.content.authority;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Enumeration;
import org.apache.log4j.Logger;
import org.dspace.content.MetadataField;
import org.dspace.content.Collection;
import org.dspace.content.ItemIterator;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.PluginManager;
/**
* 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")
*
* # 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 class ChoiceAuthorityManager
{
private static Logger log = Logger.getLogger(ChoiceAuthorityManager.class);
private static ChoiceAuthorityManager cached = null;
// map of field key to authority plugin
private Map<String,ChoiceAuthority> controller = new HashMap<String,ChoiceAuthority>();
// map of field key to presentation type
private Map<String,String> presentation = new HashMap<String,String>();
// map of field key to closed value
private Map<String,Boolean> closed = new HashMap<String,Boolean>();
private ChoiceAuthorityManager()
{
Enumeration pn = ConfigurationManager.propertyNames();
final String choicesPrefix = "choices.";
final String choicesPlugin = "choices.plugin.";
final String choicesPresentation = "choices.presentation.";
final String choicesClosed = "choices.closed.";
property:
while (pn.hasMoreElements())
{
String key = (String)pn.nextElement();
if (key.startsWith(choicesPrefix))
{
if (key.startsWith(choicesPlugin))
{
String fkey = config2fkey(key.substring(choicesPlugin.length()));
if (fkey == null)
{
log.warn("Skipping invalid ChoiceAuthority configuration property: "+key+": does not have schema.element.qualifier");
continue property;
}
// XXX FIXME maybe add sanity check, call
// MetadataField.findByElement to maek sure it's a real field.
ChoiceAuthority ma = (ChoiceAuthority)
PluginManager.getNamedPlugin(ChoiceAuthority.class, ConfigurationManager.getProperty(key));
if (ma == null)
{
log.warn("Skipping invalid configuration for "+key+" because named plugin not found: "+ConfigurationManager.getProperty(key));
continue property;
}
controller.put(fkey, ma);
log.debug("Choice Control: For field="+fkey+", Plugin="+ma);
}
else if (key.startsWith(choicesPresentation))
{
String fkey = config2fkey(key.substring(choicesPresentation.length()));
if (fkey == null)
{
log.warn("Skipping invalid ChoiceAuthority configuration property: "+key+": does not have schema.element.qualifier");
continue property;
}
presentation.put(fkey, ConfigurationManager.getProperty(key));
}
else if (key.startsWith(choicesClosed))
{
String fkey = config2fkey(key.substring(choicesClosed.length()));
if (fkey == null)
{
log.warn("Skipping invalid ChoiceAuthority configuration property: "+key+": does not have schema.element.qualifier");
continue property;
}
closed.put(fkey, new Boolean(ConfigurationManager.getBooleanProperty(key)));
}
else
log.error("Illegal configuration property: "+key);
}
}
}
/** Factory method */
public static ChoiceAuthorityManager getManager()
{
if (cached == null)
{
cached = new ChoiceAuthorityManager();
}
return cached;
}
// translate tail of configuration key (supposed to be schema.element.qual)
// into field key
private 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);
}
/**
* Wrapper that calls getMatches method of the plugin corresponding to
* the metadata field defined by schema,element,qualifier.
*
* @see ChoiceAuthority.getMatches
* @param schema schema of metadata field
* @param element element of metadata field
* @param qualifier qualifier of metadata field
* @param text user's value to match
* @param collection database ID of Collection for context (owner of Item)
* @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).
*/
public Choices getMatches(String schema, String element, String qualifier,
String query, int collection, int start, int limit, String locale)
{
return getMatches(makeFieldKey(schema, element, qualifier), query, collection, start, limit, locale);
}
/**
* Wrapper calls getMatches method of the plugin corresponding to
* the metadata field defined by single field key.
*
* @see ChoiceAuthority.getMatches
* @param fieldKey single string identifying metadata field
* @param text user's value to match
* @param collection database ID of Collection for context (owner of Item)
* @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).
*/
public Choices getMatches(String fieldKey, String query, int collection, int start, int limit, String locale)
{
ChoiceAuthority ma = controller.get(fieldKey);
if (ma == null)
throw new IllegalArgumentException("No choices plugin was configured for field \""+fieldKey+"\".");
return ma.getMatches(query, collection, start, limit, locale);
}
/**
* Wrapper that calls getBestMatch method of the plugin corresponding to
* the metadata field defined by single field key.
*
* @see ChoiceAuthority.getBestMatch
* @param fieldKey single string identifying metadata field
* @param text user's value to match
* @param collection database ID of Collection for context (owner of Item)
* @param locale explicit localization key if available, or null
* @return a Choices object (never null) with 1 or 0 values.
*/
public Choices getBestMatch(String fieldKey, String query, int collection, String locale)
{
ChoiceAuthority ma = controller.get(fieldKey);
if (ma == null)
throw new IllegalArgumentException("No choices plugin was configured for field \""+fieldKey+"\".");
return ma.getBestMatch(query, collection, locale);
}
/**
* Wrapper that calls getLabel method of the plugin corresponding to
* the metadata field defined by schema,element,qualifier.
*/
public String getLabel(String schema, String element, String qualifier,
String authKey, String locale)
{
return getLabel(makeFieldKey(schema, element, qualifier), authKey, locale);
}
/**
* Wrapper that calls getLabel method of the plugin corresponding to
* the metadata field defined by single field key.
*/
public String getLabel(String fieldKey, String authKey, String locale)
{
ChoiceAuthority ma = controller.get(fieldKey);
if (ma == null)
throw new IllegalArgumentException("No choices plugin was configured for field \""+fieldKey+"\".");
return ma.getLabel(authKey, locale);
}
/**
* Predicate, is there a Choices configuration of any kind for the
* given metadata field?
* @return true if choices are configured for this field.
*/
public boolean isChoicesConfigured(String fieldKey)
{
return controller.containsKey(fieldKey);
}
/**
* Get the presentation keyword (should be "lookup", "select" or "suggest", but this
* is an informal convention so it can be easily extended) for this field.
*
* @return configured presentation type for this field, or null if none found
*/
public String getPresentation(String fieldKey)
{
return presentation.get(fieldKey);
}
/**
* Get the configured "closed" value for this field.
*
* @return true if choices are closed for this field.
*/
public boolean isClosed(String fieldKey)
{
return closed.containsKey(fieldKey) ? closed.get(fieldKey).booleanValue() : false;
}
/**
* Construct a single key from the tuple of schema/element/qualifier
* that describes a metadata field. Punt to the function we use for
* submission UI input forms, for now.
*/
public static String makeFieldKey(String schema, String element, String qualifier)
{
return MetadataField.formKey(schema, element, qualifier);
}
/**
* Construct a single key from the "dot" notation e.g. "dc.rights"
*/
public static String makeFieldKey(String dotty)
{
return dotty.replace(".", "_");
}
/**
* Wrapper to call plugin's getVariants().
*/
public List<String> getVariants(String schema, String element, String qualifier,
String authorityKey, String language)
{
ChoiceAuthority ma = controller.get(makeFieldKey(schema, element, qualifier));
if (ma instanceof AuthorityVariantsSupport)
{
AuthorityVariantsSupport avs = (AuthorityVariantsSupport) ma;
return avs.getVariants(authorityKey, language);
}
return null;
}
}