/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.core.manager.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import javax.annotation.Resource; import org.apache.commons.collections4.trie.PatriciaTrie; import org.apache.jena.ext.com.google.common.collect.ImmutableList; import org.orcid.core.adapter.impl.IdentifierTypePOJOConverter; import org.orcid.core.adapter.impl.jsonidentifiers.ExternalIdentifierTypeConverter; import org.orcid.core.locale.LocaleManager; import org.orcid.core.manager.IdentifierTypeManager; import org.orcid.core.manager.OrcidSecurityManager; import org.orcid.core.manager.SourceManager; import org.orcid.persistence.dao.ClientDetailsDao; import org.orcid.persistence.dao.IdentifierTypeDao; import org.orcid.persistence.jpa.entities.IdentifierTypeEntity; import org.orcid.persistence.jpa.entities.SourceEntity; import org.orcid.pojo.IdentifierType; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.Lists; /** * Manages the map of external identifier types. * * Identifier types cannot be deleted, but they can be marked as deprecated. * * Identifier types are fun! In the API, they are (generally) lower case with * hyphens. In the DB they are (generally) upper case with underscores. * * @author tom * */ public class IdentifierTypeManagerImpl implements IdentifierTypeManager { @Resource private IdentifierTypeDao idTypeDao; @Resource private ClientDetailsDao clientDetailsDao; @Resource private SourceManager sourceManager; @Resource private OrcidSecurityManager securityManager; @Resource private LocaleManager localeManager; private IdentifierTypePOJOConverter adapter = new IdentifierTypePOJOConverter(); private ExternalIdentifierTypeConverter externalIdentifierTypeConverter = new ExternalIdentifierTypeConverter(); /** * Null locale will result in Locale.ENGLISH */ @Override @Cacheable("identifier-types") public IdentifierType fetchIdentifierTypeByDatabaseName(String name, Locale loc) { loc = (loc == null )? Locale.ENGLISH : loc; IdentifierTypeEntity entity = idTypeDao.getEntityByName(name); IdentifierType type = adapter.fromEntity(entity); type.setDescription(getMessage(type.getName(), loc)); return type; } /** * Returns an immutable map of API Type Name->identifierType objects. * Null locale will result in Locale.ENGLISH * */ @Override @Cacheable("identifier-types-map") public Map<String, IdentifierType> fetchIdentifierTypesByAPITypeName(Locale loc) { loc = (loc == null )? Locale.ENGLISH : loc; List<IdentifierTypeEntity> entities = idTypeDao.getEntities(); Map<String, IdentifierType> ids = new HashMap<String, IdentifierType>(); for (IdentifierTypeEntity e : entities) { IdentifierType id = adapter.fromEntity(e); id.setDescription(getMessage(id.getName(), loc)); ids.put(id.getName(), id); } return Collections.unmodifiableMap(ids); } @Override @CacheEvict(value = { "identifier-types", "identifier-types-map" }, allEntries = true) public IdentifierType createIdentifierType(IdentifierType id) { IdentifierTypeEntity entity = adapter.fromPojo(id); SourceEntity source = sourceManager.retrieveSourceEntity(); entity.setSourceClient(source.getSourceClient()); Date now = new Date(); entity.setDateCreated(now); entity.setLastModified(now); entity = idTypeDao.addIdentifierType(entity); return adapter.fromEntity(entity); } @Override @CacheEvict(value = { "identifier-types", "identifier-types-map" }, allEntries = true) public IdentifierType updateIdentifierType(IdentifierType id) { IdentifierTypeEntity entity = idTypeDao.getEntityByName(externalIdentifierTypeConverter.convertTo(id.getName(), null)); SourceEntity sourceEntity = new SourceEntity(); sourceEntity.setSourceClient(entity.getSourceClient()); securityManager.checkSource(entity); entity.setIsDeprecated(id.getDeprecated()); entity.setResolutionPrefix(id.getResolutionPrefix()); entity.setValidationRegex(id.getValidationRegex()); entity.setLastModified(new Date()); entity.setIsCaseSensitive(id.getCaseSensitive()); entity.setPrimaryUse(id.getPrimaryUse()); entity = idTypeDao.updateIdentifierType(entity); return adapter.fromEntity(entity); } private String getMessage(String type, Locale locale) { try { String key = new StringBuffer("org.orcid.jaxb.model.record.WorkExternalIdentifierType.").append(type).toString(); return localeManager.resolveMessage(key, locale, type); }catch(Exception e){ return type.replace('_', ' '); } } /** Seems pointless to base on live data - based on 2016 datadump * DOI,414627,9.49E+06,8.56E+06 EID,176888,5.52E+06,5.42E+06 PMID,65623,1.17E+06,1.16E+06 ISSN,64859,944926,493274 WOSUID,45497,1.37E+06,1.35E+06 PMC,41232,272073,270988 ISBN,39629,217805,172146 OTHER_ID,15486,203683,200963 SOURCE_WORK_ID,14091,279023,277629 ARXIV,5199,134103,130695 HANDLE,1535,26142,26069 BIBCODE,1347,83041,82412 */ //private static List<String> topTypes = Lists.newArrayList("doi","eid","pmid","issn","wosuid","pmc","isbn","other-id","arxiv","handle","bibcode"); /** * Returns an immutable list of the default identifierType objects to show. * Sorted by description * Null locale will result in Locale.ENGLISH * */ @Override @Cacheable("identifier-types-map-top") public List<IdentifierType> fetchDefaultIdentifierTypes(Locale loc) { Map<String, IdentifierType> all = this.fetchIdentifierTypesByAPITypeName(loc); SortedMap<String,IdentifierType> sorted = new TreeMap<String,IdentifierType>(); for (String s: all.keySet()) sorted.put(all.get(s).getDescription().toLowerCase(), all.get(s)); return ImmutableList.copyOf(sorted.values()); } /** * Queries the identifier name and description fields for words that START WITH query. * Returns an immutable list of matching types. * Null locale will result in Locale.ENGLISH * */ @Override @Cacheable("identifier-types-map-prefix") public List<IdentifierType> queryByPrefix(String query, Locale loc) { Map<String,IdentifierType> results = new HashMap<String,IdentifierType>(); Map<String, IdentifierType>types = fetchIdentifierTypesByAPITypeName(loc); //stick them in a trie so we can do a deep prefix search PatriciaTrie<Set<IdentifierType>> trie = new PatriciaTrie<Set<IdentifierType>>(); for (String type : types.keySet()) { IdentifierType t = types.get(type); if (!trie.containsKey(t.getName().toLowerCase())) trie.put(t.getName().toLowerCase(), new HashSet<IdentifierType>()); trie.get(t.getName().toLowerCase()).add(t); for (String s: t.getDescription().toLowerCase().split(" ")){ if (!trie.containsKey(s)) trie.put(s, new HashSet<IdentifierType>()); trie.get(s).add(t); } } //dedupe and sort SortedMap<String,Set<IdentifierType>> sorted = trie.prefixMap(query.toLowerCase()); for (Set<IdentifierType> set : sorted.values()){ for (IdentifierType t : set){ if (!results.containsKey(t.getDescription().toLowerCase())) results.put(t.getDescription().toLowerCase(),t); } } //put anything that starts with query at the top of the list. Builder<IdentifierType> builder = new Builder<IdentifierType>(); for (IdentifierType t : results.values()){ if (t.getDescription().toLowerCase().startsWith(query.toLowerCase())){ builder.add(t); } } for (IdentifierType t : results.values()){ if (!t.getDescription().toLowerCase().startsWith(query.toLowerCase())){ builder.add(t); } } return builder.build(); } }