org.orcid.frontend.web.controllers.FundingsController.java Source code

Java tutorial

Introduction

Here is the source code for org.orcid.frontend.web.controllers.FundingsController.java

Source

/**
 * =============================================================================
 *
 * 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.frontend.web.controllers;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Currency;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.orcid.core.adapter.Jaxb2JpaAdapter;
import org.orcid.core.adapter.Jpa2JaxbAdapter;
import org.orcid.core.locale.LocaleManager;
import org.orcid.core.manager.ProfileFundingManager;
import org.orcid.core.security.visibility.OrcidVisibilityDefaults;
import org.orcid.core.utils.JsonUtils;
import org.orcid.frontend.web.util.LanguagesMap;
import org.orcid.jaxb.model.message.Funding;
import org.orcid.jaxb.model.message.FundingList;
import org.orcid.jaxb.model.message.FundingType;
import org.orcid.jaxb.model.message.OrcidActivities;
import org.orcid.jaxb.model.message.OrcidProfile;
import org.orcid.jaxb.model.record_rc1.FundingExternalIdentifier;
import org.orcid.jaxb.model.record_rc1.Relationship;
import org.orcid.persistence.dao.OrgDisambiguatedDao;
import org.orcid.persistence.dao.OrgDisambiguatedSolrDao;
import org.orcid.persistence.dao.ProfileDao;
import org.orcid.persistence.jpa.entities.CountryIsoEntity;
import org.orcid.persistence.jpa.entities.OrgDisambiguatedEntity;
import org.orcid.persistence.jpa.entities.ProfileEntity;
import org.orcid.persistence.jpa.entities.ProfileFundingEntity;
import org.orcid.persistence.jpa.entities.SourceEntity;
import org.orcid.persistence.solr.entities.OrgDisambiguatedSolrDocument;
import org.orcid.pojo.FundingExternalIdentifiers;
import org.orcid.pojo.ajaxForm.Contributor;
import org.orcid.pojo.ajaxForm.Date;
import org.orcid.pojo.ajaxForm.FundingExternalIdentifierForm;
import org.orcid.pojo.ajaxForm.FundingForm;
import org.orcid.pojo.ajaxForm.FundingTitleForm;
import org.orcid.pojo.ajaxForm.OrgDefinedFundingSubType;
import org.orcid.pojo.ajaxForm.PojoUtil;
import org.orcid.pojo.ajaxForm.Text;
import org.orcid.pojo.ajaxForm.TranslatedTitle;
import org.orcid.pojo.ajaxForm.Visibility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author Angel Montenegro
 */
@Controller("fundingsController")
@RequestMapping(value = { "/fundings" })
public class FundingsController extends BaseWorkspaceController {
    private static final Logger LOGGER = LoggerFactory.getLogger(FundingsController.class);
    private static final String GRANT_MAP = "GRANT_MAP";
    private static final Pattern LANGUAGE_CODE = Pattern.compile("([a-zA-Z]{2})(_[a-zA-Z]{2}){0,2}");
    private static final String DEFAULT_FUNDING_EXTERNAL_IDENTIFIER_TYPE = "Grant number";
    private static final String DEFAULT_FUNDING_EXTERNAL_IDENTIFIER_TYPE_CODE = "grant_number";

    @Resource
    private ProfileDao profileDao;

    @Resource
    ProfileFundingManager profileFundingManager;

    @Resource
    private Jaxb2JpaAdapter jaxb2JpaAdapter;

    @Resource
    private Jpa2JaxbAdapter jpa2JaxbAdapter;

    @Resource
    private OrgDisambiguatedSolrDao orgDisambiguatedSolrDao;

    @Resource
    private OrgDisambiguatedDao orgDisambiguatedDao;

    @Resource
    private LocaleManager localeManager;

    @Resource(name = "languagesMap")
    private LanguagesMap lm;

    public void setLocaleManager(LocaleManager localeManager) {
        this.localeManager = localeManager;
    }

    /**
     * Returns a blank funding form
     * */
    @RequestMapping(value = "/funding.json", method = RequestMethod.GET)
    public @ResponseBody FundingForm getFunding() {
        FundingForm result = new FundingForm();
        result.setAmount(new Text());
        result.setCurrencyCode(Text.valueOf(""));
        result.setDescription(new Text());
        result.setFundingName(new Text());
        result.setFundingType(Text.valueOf(""));
        result.setSourceName(new String());

        OrgDefinedFundingSubType subtype = new OrgDefinedFundingSubType();
        subtype.setAlreadyIndexed(false);
        subtype.setSubtype(Text.valueOf(""));
        result.setOrganizationDefinedFundingSubType(subtype);

        FundingTitleForm title = new FundingTitleForm();
        title.setTitle(new Text());
        TranslatedTitle tt = new TranslatedTitle();
        tt.setContent(new String());
        tt.setLanguageCode(new String());
        tt.setLanguageName(new String());
        title.setTranslatedTitle(tt);
        result.setFundingTitle(title);
        result.setUrl(new Text());
        OrcidProfile profile = getEffectiveProfile();
        Visibility v = Visibility
                .valueOf(profile.getOrcidInternal().getPreferences().getActivitiesVisibilityDefault().getValue());
        result.setVisibility(v);
        Date startDate = new Date();
        result.setStartDate(startDate);
        startDate.setDay("");
        startDate.setMonth("");
        startDate.setYear("");
        Date endDate = new Date();
        result.setEndDate(endDate);
        endDate.setDay("");
        endDate.setMonth("");
        endDate.setYear("");

        // Set empty contributor
        Contributor contr = new Contributor();
        List<Contributor> contrList = new ArrayList<Contributor>();
        Text rText = new Text();
        rText.setValue("");
        contr.setContributorRole(rText);
        Text sText = new Text();
        sText.setValue("");
        contr.setContributorSequence(sText);
        contrList.add(contr);
        result.setContributors(contrList);

        // Set empty external identifier
        List<FundingExternalIdentifierForm> emptyExternalIdentifiers = new ArrayList<FundingExternalIdentifierForm>();
        FundingExternalIdentifierForm f = new FundingExternalIdentifierForm();
        f.setType(Text.valueOf(DEFAULT_FUNDING_EXTERNAL_IDENTIFIER_TYPE));
        f.setUrl(new Text());
        f.setValue(new Text());
        f.setRelationship(Text.valueOf(Relationship.SELF.value()));
        emptyExternalIdentifiers.add(f);
        result.setExternalIdentifiers(emptyExternalIdentifiers);

        result.setCity(new Text());
        result.setCountry(Text.valueOf(""));
        result.setRegion(new Text());

        return result;
    }

    /**
     * Deletes a funding
     * */
    @RequestMapping(value = "/funding.json", method = RequestMethod.DELETE)
    public @ResponseBody FundingForm deleteFundingJson(HttpServletRequest request,
            @RequestBody FundingForm funding) {
        Funding delFunding = funding.toOrcidFunding();
        OrcidProfile currentProfile = getEffectiveProfile();
        FundingList fundings = currentProfile.getOrcidActivities() == null ? null
                : currentProfile.getOrcidActivities().getFundings();
        FundingForm deletedFunding = new FundingForm();
        if (fundings != null) {
            List<Funding> fundingList = fundings.getFundings();
            Iterator<Funding> iterator = fundingList.iterator();
            while (iterator.hasNext()) {
                Funding orcidFunding = iterator.next();
                if (delFunding.equals(orcidFunding)) {
                    iterator.remove();
                    deletedFunding = funding;
                }
            }
            fundings.setFundings(fundingList);
            currentProfile.getOrcidActivities().setFundings(fundings);
            profileFundingManager.removeProfileFunding(currentProfile.getOrcidIdentifier().getPath(),
                    Long.valueOf(funding.getPutCode().getValue()));
        }
        return deletedFunding;
    }

    /**
     * List fundings associated with a profile
     * */
    @RequestMapping(value = "/fundingIds.json", method = RequestMethod.GET)
    public @ResponseBody List<String> getFundingsJson(HttpServletRequest request) {
        // Get cached profile
        List<String> fundingIds = createFundingIdList(request);
        return fundingIds;
    }

    /**
     * Create a funding id list and sorts a map associated with the list in in
     * the session
     * 
     */
    private List<String> createFundingIdList(HttpServletRequest request) {
        OrcidProfile currentProfile = getEffectiveProfile();
        Map<String, String> languages = lm.buildLanguageMap(getUserLocale(), false);
        FundingList fundings = currentProfile.getOrcidActivities() == null ? null
                : currentProfile.getOrcidActivities().getFundings();

        HashMap<String, FundingForm> fundingsMap = new HashMap<>();
        List<String> fundingIds = new ArrayList<String>();
        if (fundings != null) {
            for (Funding funding : fundings.getFundings()) {
                try {
                    FundingForm form = FundingForm.valueOf(funding);
                    //XXX: Enhance the external identifiers with the new relationship field
                    ProfileFundingEntity profileFunding = profileFundingManager
                            .getProfileFundingEntity(Long.valueOf(funding.getPutCode()));
                    if (!PojoUtil.isEmpty(profileFunding.getExternalIdentifiersJson())) {
                        FundingExternalIdentifiers extIdsPojo = JsonUtils.readObjectFromJsonString(
                                profileFunding.getExternalIdentifiersJson(), FundingExternalIdentifiers.class);
                        org.orcid.jaxb.model.record_rc1.FundingExternalIdentifiers fundingExternalIdentifiers = extIdsPojo
                                .toRecordPojo();
                        enhanceExternalIdentifiers(form, fundingExternalIdentifiers);
                    }

                    if (funding.getType() != null) {
                        form.setFundingTypeForDisplay(getMessage(
                                buildInternationalizationKey(FundingType.class, funding.getType().value())));
                    }
                    // Set translated title language name
                    if (!(funding.getTitle().getTranslatedTitle() == null)
                            && !StringUtils.isEmpty(funding.getTitle().getTranslatedTitle().getLanguageCode())) {
                        String languageName = languages
                                .get(funding.getTitle().getTranslatedTitle().getLanguageCode());
                        form.getFundingTitle().getTranslatedTitle().setLanguageName(languageName);
                    }

                    // Set the formatted amount
                    if (funding.getAmount() != null && StringUtils.isNotBlank(funding.getAmount().getContent())) {
                        BigDecimal bigDecimal = new BigDecimal(funding.getAmount().getContent());
                        String formattedAmount = formatAmountString(bigDecimal);
                        form.setAmount(Text.valueOf(formattedAmount));
                    }
                    form.setCountryForDisplay(getMessage(buildInternationalizationKey(CountryIsoEntity.class,
                            funding.getOrganization().getAddress().getCountry().name())));
                    fundingsMap.put(funding.getPutCode(), form);
                    fundingIds.add(funding.getPutCode());
                } catch (Exception e) {
                    LOGGER.error("Failed to parse as Funding. Put code" + funding.getPutCode(), e);
                }
            }
            request.getSession().setAttribute(GRANT_MAP, fundingsMap);
        }
        return fundingIds;
    }

    /**
     * List fundings associated with a profile
     * */
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/fundings.json", method = RequestMethod.GET)
    public @ResponseBody List<FundingForm> getFundingsJson(HttpServletRequest request,
            @RequestParam(value = "fundingIds") String fundingIdsStr) {
        List<FundingForm> fundingList = new ArrayList<>();
        FundingForm funding = null;
        String[] fundingIds = fundingIdsStr.split(",");

        if (fundingIds != null) {
            HashMap<String, FundingForm> fundingsMap = (HashMap<String, FundingForm>) request.getSession()
                    .getAttribute(GRANT_MAP);
            // this should never happen, but just in case.
            if (fundingsMap == null) {
                createFundingIdList(request);
                fundingsMap = (HashMap<String, FundingForm>) request.getSession().getAttribute(GRANT_MAP);
            }
            for (String fundingId : fundingIds) {
                funding = fundingsMap.get(fundingId);
                fundingList.add(funding);
            }
        }

        return fundingList;
    }

    /**
     * List fundings associated with a profile
     * */
    @RequestMapping(value = "/getFunding.json", method = RequestMethod.GET)
    public @ResponseBody FundingForm getFundingJson(@RequestParam(value = "fundingId") Long fundingId) {
        if (fundingId == null)
            return null;
        ProfileFundingEntity profileFunding = profileFundingManager.getProfileFundingEntity(fundingId);
        if (profileFunding == null) {
            return null;
        }
        Map<String, String> languages = lm.buildLanguageMap(getUserLocale(), false);
        Funding funding = jpa2JaxbAdapter.getFunding(profileFunding);
        FundingForm form = FundingForm.valueOf(funding);
        //XXX: Enhance the external identifiers with the new relationship field
        if (!PojoUtil.isEmpty(profileFunding.getExternalIdentifiersJson())) {
            FundingExternalIdentifiers extIdsPojo = JsonUtils.readObjectFromJsonString(
                    profileFunding.getExternalIdentifiersJson(), FundingExternalIdentifiers.class);
            org.orcid.jaxb.model.record_rc1.FundingExternalIdentifiers fundingExternalIdentifiers = extIdsPojo
                    .toRecordPojo();
            enhanceExternalIdentifiers(form, fundingExternalIdentifiers);
        }
        if (funding.getType() != null) {
            form.setFundingTypeForDisplay(
                    getMessage(buildInternationalizationKey(FundingType.class, funding.getType().value())));
        }
        // Set translated title language name
        if (!(funding.getTitle().getTranslatedTitle() == null)
                && !StringUtils.isEmpty(funding.getTitle().getTranslatedTitle().getLanguageCode())) {
            String languageName = languages.get(funding.getTitle().getTranslatedTitle().getLanguageCode());
            form.getFundingTitle().getTranslatedTitle().setLanguageName(languageName);
        }

        // Set the formatted amount
        if (funding.getAmount() != null && StringUtils.isNotBlank(funding.getAmount().getContent())) {
            BigDecimal bigDecimal = new BigDecimal(funding.getAmount().getContent());
            String formattedAmount = formatAmountString(bigDecimal);
            form.setAmount(Text.valueOf(formattedAmount));
        }

        form.setCountryForDisplay(getMessage(buildInternationalizationKey(CountryIsoEntity.class,
                funding.getOrganization().getAddress().getCountry().name())));
        return form;
    }

    /**
     * Persist a funding object on database
     * */
    @RequestMapping(value = "/funding.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm postFunding(@RequestBody FundingForm funding) throws Exception {
        // Reset errors
        funding.setErrors(new ArrayList<String>());
        // Remove empty external identifiers
        removeEmptyExternalIds(funding);

        validateName(funding);
        validateAmount(funding);
        validateCurrency(funding);
        validateTitle(funding);
        validateTranslatedTitle(funding);
        validateDescription(funding);
        validateUrl(funding);
        validateDates(funding);
        validateExternalIdentifiers(funding);
        validateType(funding);
        validateOrganizationDefinedType(funding);
        validateCity(funding);
        validateRegion(funding);
        validateCountry(funding);

        copyErrors(funding.getCity(), funding);
        copyErrors(funding.getRegion(), funding);
        copyErrors(funding.getCountry(), funding);
        copyErrors(funding.getFundingName(), funding);
        copyErrors(funding.getAmount(), funding);
        copyErrors(funding.getCurrencyCode(), funding);
        copyErrors(funding.getFundingTitle().getTitle(), funding);
        copyErrors(funding.getDescription(), funding);
        copyErrors(funding.getUrl(), funding);
        copyErrors(funding.getFundingType(), funding);

        if (funding.getStartDate() != null)
            copyErrors(funding.getStartDate(), funding);

        if (funding.getEndDate() != null)
            copyErrors(funding.getEndDate(), funding);

        if (funding.getFundingTitle().getTranslatedTitle() != null)
            copyErrors(funding.getFundingTitle().getTranslatedTitle(), funding);

        if (funding.getOrganizationDefinedFundingSubType() != null)
            copyErrors(funding.getOrganizationDefinedFundingSubType().getSubtype(), funding);

        for (FundingExternalIdentifierForm extId : funding.getExternalIdentifiers()) {
            if (extId.getType() != null)
                copyErrors(extId.getType(), funding);
            if (extId.getUrl() != null)
                copyErrors(extId.getUrl(), funding);
            if (extId.getValue() != null)
                copyErrors(extId.getValue(), funding);
        }

        // If there are no errors, persist to DB
        if (funding.getErrors().isEmpty()) {
            if (PojoUtil.isEmpty(funding.getPutCode())) {
                addFunding(funding);
            } else {
                editFunding(funding);
            }
        }

        return funding;
    }

    private void addFunding(FundingForm funding) throws Exception {
        // Set the right value for the amount
        setAmountWithTheCorrectFormat(funding);
        // Set the credit name
        setContributorsCreditName(funding);
        // Set default type for external identifiers
        setTypeToExternalIdentifiers(funding);
        // Update on database
        ProfileEntity userProfile = profileDao.find(getEffectiveUserOrcid());
        ProfileFundingEntity profileFundingEntity = jaxb2JpaAdapter
                .getNewProfileFundingEntity(funding.toOrcidFunding(), userProfile);
        profileFundingEntity.setSource(new SourceEntity(userProfile));
        //XXX: Enhance the external identifiers with the new relationship field
        enhanceExternalIdentifiers(profileFundingEntity, funding);

        // Persists the profile funding object
        ProfileFundingEntity newProfileFunding = profileFundingManager.addProfileFunding(profileFundingEntity);

        // Transform it back into a OrcidGrant to add it into the cached
        // object
        Funding newFunding = jpa2JaxbAdapter.getFunding(newProfileFunding);
        // Update the fundings on the cached object
        OrcidProfile currentProfile = getEffectiveProfile();
        // Initialize activities if needed
        if (currentProfile.getOrcidActivities() == null) {
            currentProfile.setOrcidActivities(new OrcidActivities());
        }
        // Initialize fundings if needed
        if (currentProfile.getOrcidActivities().getFundings() == null) {
            currentProfile.getOrcidActivities().setFundings(new FundingList());
        }

        // make the newly added funding the default
        profileFundingManager.updateToMaxDisplay(currentProfile.getOrcidIdentifier().getPath(),
                newProfileFunding.getId());

        // Set the new funding into the cached object
        currentProfile.getOrcidActivities().getFundings().getFundings().add(newFunding);

        // Send the new funding sub type for indexing
        if (funding.getOrganizationDefinedFundingSubType() != null
                && !PojoUtil.isEmpty(funding.getOrganizationDefinedFundingSubType().getSubtype())
                && !funding.getOrganizationDefinedFundingSubType().isAlreadyIndexed())
            profileFundingManager.addFundingSubType(
                    funding.getOrganizationDefinedFundingSubType().getSubtype().getValue(),
                    getEffectiveUserOrcid());
    }

    private void editFunding(FundingForm funding) throws Exception {
        // Set the right value for the amount
        setAmountWithTheCorrectFormat(funding);
        // Set the credit name
        setContributorsCreditName(funding);
        // Set default type for external identifiers
        setTypeToExternalIdentifiers(funding);
        // Update profile_funding data
        ProfileFundingEntity updatedProfileFundingEntity = jaxb2JpaAdapter
                .getUpdatedProfileFundingEntity(funding.toOrcidFunding());
        //XXX: Enhance the external identifiers with the new relationship field
        enhanceExternalIdentifiers(updatedProfileFundingEntity, funding);
        updatedProfileFundingEntity = profileFundingManager.updateProfileFunding(updatedProfileFundingEntity);
        // Transform it back into a OrcidGrant to add it into the cached
        // object
        Funding updatedFunding = jpa2JaxbAdapter.getFunding(updatedProfileFundingEntity);
        // Update the funding on the cached object
        OrcidProfile currentProfile = getEffectiveProfile();

        if (!currentProfile.getOrcidIdentifier().getPath().equals(funding.getSource()))
            throw new Exception(getMessage("web.orcid.activity_incorrectsource.exception"));

        // Initialize activities if needed
        if (currentProfile.getOrcidActivities() == null) {
            currentProfile.setOrcidActivities(new OrcidActivities());
        }
        // Initialize fundings if needed
        if (currentProfile.getOrcidActivities().getFundings() == null) {
            currentProfile.getOrcidActivities().setFundings(new FundingList());
        }

        // Set the new funding into the cached object
        for (int i = 0; i < currentProfile.getOrcidActivities().getFundings().getFundings().size(); i++) {
            Funding existingFunding = currentProfile.getOrcidActivities().getFundings().getFundings().get(i);
            if (updatedFunding.getPutCode().equals(existingFunding.getPutCode())) {
                currentProfile.getOrcidActivities().getFundings().getFundings().set(i, updatedFunding);
                break;
            }
        }

        // Send the new funding sub type for indexing
        if (funding.getOrganizationDefinedFundingSubType() != null
                && !PojoUtil.isEmpty(funding.getOrganizationDefinedFundingSubType().getSubtype())
                && !funding.getOrganizationDefinedFundingSubType().isAlreadyIndexed())
            profileFundingManager.addFundingSubType(
                    funding.getOrganizationDefinedFundingSubType().getSubtype().getValue(),
                    getEffectiveUserOrcid());
    }

    private void removeEmptyExternalIds(FundingForm funding) {
        List<FundingExternalIdentifierForm> extIds = funding.getExternalIdentifiers();
        List<FundingExternalIdentifierForm> updatedExtIds = new ArrayList<FundingExternalIdentifierForm>();
        if (extIds != null) {
            // For all external identifiers
            for (FundingExternalIdentifierForm extId : extIds) {
                // Keep only the ones that contains a value or url
                if (!PojoUtil.isEmpty(extId.getValue()) || !PojoUtil.isEmpty(extId.getUrl())) {
                    updatedExtIds.add(extId);
                }
            }
        }
        funding.setExternalIdentifiers(updatedExtIds);
    }

    private void setContributorsCreditName(FundingForm funding) {
        OrcidProfile profile = getEffectiveProfile();
        String creditName = null;
        Visibility creditNameVisibility = null;
        if (profile.getOrcidBio() != null && profile.getOrcidBio().getPersonalDetails() != null
                && profile.getOrcidBio().getPersonalDetails().getCreditName() != null) {
            creditName = profile.getOrcidBio().getPersonalDetails().getCreditName().getContent();
            creditNameVisibility = Visibility
                    .valueOf(profile.getOrcidBio().getPersonalDetails().getCreditName().getVisibility());
        }
        if (funding != null && funding.getContributors() != null && !funding.getContributors().isEmpty()) {
            for (Contributor contributor : funding.getContributors()) {
                if (!PojoUtil.areAllEmtpy(contributor.getContributorRole(), contributor.getContributorSequence())) {
                    if (!PojoUtil.isEmpty(creditName))
                        contributor.setCreditName(Text.valueOf(creditName));
                    if (creditNameVisibility != null) {
                        contributor.setCreditNameVisibility(creditNameVisibility);
                    } else {
                        contributor.setCreditNameVisibility(
                                Visibility.valueOf(OrcidVisibilityDefaults.CREDIT_NAME_DEFAULT.getVisibility()));
                    }
                }
            }
        }
    }

    private void setTypeToExternalIdentifiers(FundingForm funding) {
        if (funding == null || funding.getExternalIdentifiers() == null
                || funding.getExternalIdentifiers().isEmpty())
            return;
        for (FundingExternalIdentifierForm extId : funding.getExternalIdentifiers()) {
            extId.setType(Text.valueOf(DEFAULT_FUNDING_EXTERNAL_IDENTIFIER_TYPE_CODE));
        }
    }

    private void setAmountWithTheCorrectFormat(FundingForm funding) throws Exception {
        if (!PojoUtil.isEmpty(funding.getAmount())) {
            String amount = funding.getAmount().getValue();
            BigDecimal bigDecimal = getAmountAsBigDecimal(amount);
            funding.setAmount(Text.valueOf(bigDecimal.toString()));
        }
    }

    /**
     * Saves an affiliation
     * */
    @RequestMapping(value = "/funding.json", method = RequestMethod.PUT)
    public @ResponseBody FundingForm updateProfileFundingJson(HttpServletRequest request,
            @RequestBody FundingForm fundingForm) {
        // Get cached profile
        OrcidProfile currentProfile = getEffectiveProfile();
        FundingList orcidGrants = currentProfile.getOrcidActivities() == null ? null
                : currentProfile.getOrcidActivities().getFundings();
        if (orcidGrants != null) {
            List<Funding> fundings = orcidGrants.getFundings();
            if (fundings != null) {
                for (Funding funding : fundings) {
                    if (funding.getPutCode().equals(fundingForm.getPutCode().getValue())) {
                        // Update the privacy of the funding
                        profileFundingManager.updateProfileFundingVisibility(
                                currentProfile.getOrcidIdentifier().getPath(),
                                Long.valueOf(fundingForm.getPutCode().getValue()),
                                fundingForm.getVisibility().getVisibility());
                    }
                }
            }
        }
        return fundingForm;
    }

    /**
     * Transforms a string into a BigDecimal
     * 
     * @param amount
     * @return a BigDecimal containing the given amount
     * @throws Exception
     *             if the amount cannot be correctly parse into a BigDecimal
     * */
    public BigDecimal getAmountAsBigDecimal(String amount) throws Exception {
        Locale locale = getUserLocale();
        return getAmountAsBigDecimal(amount, locale);
    }

    /**
     * Transforms a string into a BigDecimal
     * 
     * @param amount
     * @param locale
     * @return a BigDecimal containing the given amount
     * @throws Exception
     *             if the amount cannot be correctly parse into a BigDecimal
     * */
    public BigDecimal getAmountAsBigDecimal(String amount, Locale locale) throws Exception {
        try {
            ParsePosition parsePosition = new ParsePosition(0);
            DecimalFormat numberFormat = (DecimalFormat) NumberFormat.getNumberInstance(locale);
            DecimalFormatSymbols symbols = numberFormat.getDecimalFormatSymbols();
            /**
             * When spaces are allowed, the grouping separator is the character
             * 160, which is a non-breaking space So, lets change it so it uses
             * the default space as a separator
             * */
            if (symbols.getGroupingSeparator() == 160) {
                symbols.setGroupingSeparator(' ');
            }
            numberFormat.setDecimalFormatSymbols(symbols);
            Number number = numberFormat.parse(amount, parsePosition);
            if (number == null || parsePosition.getIndex() != amount.length()) {
                throw new Exception();
            }
            return new BigDecimal(number.toString());
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Get a string with the proper amount format
     * 
     * @param local
     * @return an example string showing how the amount should be entered
     * */
    private String getSampleAmountInProperFormat(Locale locale) {
        double example = 1234567.89;
        NumberFormat numberFormatExample = NumberFormat.getNumberInstance(locale);
        return numberFormatExample.format(example);
    }

    /**
     * Format a big decimal based on a locale
     * 
     * @param bigDecimal
     * @param currencyCode
     * @return a string with the number formatted based on the locale
     * */
    private String formatAmountString(BigDecimal bigDecimal) {
        NumberFormat numberFormat = NumberFormat.getNumberInstance(localeManager.getLocale());
        return numberFormat.format(bigDecimal);
    }

    /**
     * Validators
     * */
    @RequestMapping(value = "/funding/amountValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateAmount(@RequestBody FundingForm funding) {
        funding.getAmount().setErrors(new ArrayList<String>());
        if (!PojoUtil.isEmpty(funding.getAmount())) {
            // Validate amount
            String amount = funding.getAmount().getValue();
            Locale locale = getUserLocale();
            try {
                getAmountAsBigDecimal(amount, locale);
            } catch (Exception pe) {
                setError(funding.getAmount(), "Invalid.fundings.amount", getSampleAmountInProperFormat(locale));
            }
            // Validate if currency code is selected
            if (PojoUtil.isEmpty(funding.getCurrencyCode()))
                setError(funding.getAmount(), "Invalid.fundings.currency");
        } else if (!PojoUtil.isEmpty(funding.getCurrencyCode())) {
            setError(funding.getAmount(), "Invalid.fundings.currency_not_empty");
        }
        return funding;
    }

    @RequestMapping(value = "/funding/currencyValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateCurrency(@RequestBody FundingForm funding) {
        funding.getCurrencyCode().setErrors(new ArrayList<String>());
        if (!PojoUtil.isEmpty(funding.getCurrencyCode())) {
            try {
                Currency.getInstance(funding.getCurrencyCode().getValue());
            } catch (IllegalArgumentException iae) {
                setError(funding.getCurrencyCode(), "Invalid.fundings.currency");
            }
        }
        return funding;
    }

    @RequestMapping(value = "/funding/orgNameValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateName(@RequestBody FundingForm funding) {
        funding.getFundingName().setErrors(new ArrayList<String>());
        if (funding.getFundingName().getValue() == null
                || funding.getFundingName().getValue().trim().length() == 0) {
            setError(funding.getFundingName(), "NotBlank.fundings.name");
        } else {
            if (funding.getFundingName().getValue().trim().length() > 1000) {
                setError(funding.getFundingName(), "common.length_less_1000");
            }
        }
        return funding;
    }

    @RequestMapping(value = "/funding/titleValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateTitle(@RequestBody FundingForm funding) {
        funding.getFundingTitle().getTitle().setErrors(new ArrayList<String>());
        if (PojoUtil.isEmpty(funding.getFundingTitle().getTitle())) {
            setError(funding.getFundingTitle().getTitle(), "NotBlank.fundings.title");
        } else {
            if (funding.getFundingTitle().getTitle().getValue().length() > 1000)
                setError(funding.getFundingTitle().getTitle(), "common.length_less_1000");
        }
        return funding;
    }

    @RequestMapping(value = "/funding/translatedTitleValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateTranslatedTitle(@RequestBody FundingForm funding) {
        if (funding.getFundingTitle().getTranslatedTitle() != null) {
            funding.getFundingTitle().getTranslatedTitle().setErrors(new ArrayList<String>());
            String content = funding.getFundingTitle().getTranslatedTitle().getContent();
            String code = funding.getFundingTitle().getTranslatedTitle().getLanguageCode();

            if (!StringUtils.isEmpty(content)) {
                if (!StringUtils.isEmpty(code)) {
                    if (!LANGUAGE_CODE.matcher(funding.getFundingTitle().getTranslatedTitle().getLanguageCode())
                            .matches()) {
                        setError(funding.getFundingTitle().getTranslatedTitle(),
                                "manual_funding_form_contents.invalid_language_code");
                    }
                } else {
                    setError(funding.getFundingTitle().getTranslatedTitle(),
                            "manual_funding_form_contents.empty_code");
                }
                if (content.length() > 1000) {
                    setError(funding.getFundingTitle().getTranslatedTitle(), "common.length_less_1000");
                }
            } else {
                if (!StringUtils.isEmpty(code)) {
                    setError(funding.getFundingTitle().getTranslatedTitle(),
                            "manual_funding_form_contents.empty_translation");
                }
            }
        }
        return funding;
    }

    @RequestMapping(value = "/funding/descriptionValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateDescription(@RequestBody FundingForm funding) {
        funding.getDescription().setErrors(new ArrayList<String>());
        if (!PojoUtil.isEmpty(funding.getDescription()) && funding.getDescription().getValue().length() > 5000)
            setError(funding.getDescription(), "fundings.length_less_5000");
        return funding;
    }

    @RequestMapping(value = "/funding/urlValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateUrl(@RequestBody FundingForm funding) {
        validateUrl(funding.getUrl());
        return funding;
    }

    @RequestMapping(value = "/funding/datesValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateDates(@RequestBody FundingForm funding) {
        boolean hasError = false;

        if (funding.getStartDate() != null) {
            funding.getStartDate().setErrors(new ArrayList<String>());

            if (!PojoUtil.isEmpty(funding.getStartDate().getMonth())
                    && PojoUtil.isEmpty(funding.getStartDate().getYear())) {
                setError(funding.getStartDate(), "common.dates.invalid");
                hasError = true;
            }
        }

        if (funding.getEndDate() != null) {
            funding.getEndDate().setErrors(new ArrayList<String>());

            if (!PojoUtil.isEmpty(funding.getEndDate().getMonth())
                    && PojoUtil.isEmpty(funding.getEndDate().getYear())) {
                setError(funding.getEndDate(), "common.dates.invalid");
                hasError = true;
            }
        }

        if (!hasError && !PojoUtil.isEmpty(funding.getStartDate()) && !PojoUtil.isEmpty(funding.getEndDate())) {
            if (funding.getStartDate().toJavaDate().after(funding.getEndDate().toJavaDate()))
                setError(funding.getEndDate(), "fundings.endDate.after");
        }
        return funding;
    }

    @RequestMapping(value = "/funding/externalIdentifiersValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateExternalIdentifiers(@RequestBody FundingForm funding) {
        if (funding.getExternalIdentifiers() != null && !funding.getExternalIdentifiers().isEmpty()) {
            for (FundingExternalIdentifierForm extId : funding.getExternalIdentifiers()) {
                extId.setErrors(new ArrayList<String>());
                if (extId.getType() != null)
                    extId.getType().setErrors(new ArrayList<String>());
                if (extId.getUrl() != null)
                    extId.getUrl().setErrors(new ArrayList<String>());
                if (!PojoUtil.isEmpty(extId.getType()) && extId.getType().getValue().length() > 255)
                    setError(extId.getType(), "fundings.lenght_less_255");
                if (!PojoUtil.isEmpty(extId.getUrl()) && extId.getUrl().getValue().length() > 350)
                    setError(extId.getUrl(), "fundings.length_less_350");
                if (!PojoUtil.isEmpty(extId.getValue()) && extId.getValue().getValue().length() > 2084)
                    setError(extId.getValue(), "fundings.length_less_2084");
            }
        }
        return funding;
    }

    @RequestMapping(value = "/funding/typeValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateType(@RequestBody FundingForm funding) {
        funding.getFundingType().setErrors(new ArrayList<String>());
        if (PojoUtil.isEmpty(funding.getFundingType())) {
            setError(funding.getFundingType(), "NotBlank.fundings.type");
        } else {
            try {
                FundingType.fromValue(funding.getFundingType().getValue());
            } catch (IllegalArgumentException iae) {
                setError(funding.getFundingType(), "Invalid.fundings.type");
            }
        }
        return funding;
    }

    @RequestMapping(value = "/funding/organizationDefinedTypeValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateOrganizationDefinedType(@RequestBody FundingForm funding) {
        if (funding.getOrganizationDefinedFundingSubType() == null)
            funding.setOrganizationDefinedFundingSubType(new OrgDefinedFundingSubType());
        if (funding.getOrganizationDefinedFundingSubType().getSubtype() == null)
            funding.getOrganizationDefinedFundingSubType().setSubtype(Text.valueOf(""));
        funding.getOrganizationDefinedFundingSubType().getSubtype().setErrors(new ArrayList<String>());
        if (!PojoUtil.isEmpty(funding.getOrganizationDefinedFundingSubType().getSubtype())) {
            String value = funding.getOrganizationDefinedFundingSubType().getSubtype().getValue();
            if (value.length() > 255)
                setError(funding.getOrganizationDefinedFundingSubType().getSubtype(), "fundings.lenght_less_255");
        }
        return funding;
    }

    @RequestMapping(value = "/funding/cityValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateCity(@RequestBody FundingForm funding) {
        funding.getCity().setErrors(new ArrayList<String>());
        if (funding.getCity().getValue() == null || funding.getCity().getValue().trim().length() == 0) {
            setError(funding.getCity(), "NotBlank.fundings.city");
        } else {
            if (funding.getCity().getValue().trim().length() > 1000) {
                setError(funding.getCity(), "common.length_less_1000");
            }
        }
        return funding;
    }

    @RequestMapping(value = "/funding/regionValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateRegion(@RequestBody FundingForm funding) {
        funding.getRegion().setErrors(new ArrayList<String>());
        if (funding.getRegion().getValue() != null && funding.getRegion().getValue().trim().length() > 1000) {
            setError(funding.getRegion(), "common.length_less_1000");
        }
        return funding;
    }

    @RequestMapping(value = "/funding/countryValidate.json", method = RequestMethod.POST)
    public @ResponseBody FundingForm validateCountry(@RequestBody FundingForm funding) {
        funding.getCountry().setErrors(new ArrayList<String>());
        if (funding.getCountry().getValue() == null || funding.getCountry().getValue().trim().length() == 0) {
            setError(funding.getCountry(), "common.country.not_blank");
        }
        return funding;
    }

    /**
     * Typeahead
     * */

    /**
     * Search DB for disambiguated affiliations to suggest to user
     */
    @RequestMapping(value = "/disambiguated/name/{query}", method = RequestMethod.GET)
    public @ResponseBody List<Map<String, String>> searchDisambiguated(@PathVariable("query") String query,
            @RequestParam(value = "limit") int limit, @RequestParam(value = "funders-only") boolean fundersOnly) {
        List<Map<String, String>> datums = new ArrayList<>();
        for (OrgDisambiguatedSolrDocument orgDisambiguatedDocument : orgDisambiguatedSolrDao.getOrgs(query, 0,
                limit, fundersOnly)) {
            Map<String, String> datum = createDatumFromOrgDisambiguated(orgDisambiguatedDocument);
            datums.add(datum);
        }
        return datums;
    }

    private Map<String, String> createDatumFromOrgDisambiguated(
            OrgDisambiguatedSolrDocument orgDisambiguatedDocument) {
        Map<String, String> datum = new HashMap<>();
        datum.put("value", orgDisambiguatedDocument.getOrgDisambiguatedName());
        datum.put("city", orgDisambiguatedDocument.getOrgDisambiguatedCity());
        datum.put("region", orgDisambiguatedDocument.getOrgDisambiguatedRegion());
        datum.put("country", orgDisambiguatedDocument.getOrgDisambiguatedCountry());
        datum.put("orgType", orgDisambiguatedDocument.getOrgDisambiguatedType());
        datum.put("disambiguatedFundingIdentifier",
                Long.toString(orgDisambiguatedDocument.getOrgDisambiguatedId()));
        return datum;
    }

    /**
     * fetch disambiguated by id
     */
    @RequestMapping(value = "/disambiguated/id/{id}", method = RequestMethod.GET)
    public @ResponseBody Map<String, String> getDisambiguated(@PathVariable("id") Long id) {
        OrgDisambiguatedEntity orgDisambiguatedEntity = orgDisambiguatedDao.find(id);
        Map<String, String> datum = new HashMap<>();
        datum.put("value", orgDisambiguatedEntity.getName());
        datum.put("city", orgDisambiguatedEntity.getCity());
        datum.put("region", orgDisambiguatedEntity.getRegion());
        if (orgDisambiguatedEntity.getCountry() != null)
            datum.put("country", orgDisambiguatedEntity.getCountry().value());
        datum.put("sourceId", orgDisambiguatedEntity.getSourceId());
        datum.put("sourceType", orgDisambiguatedEntity.getSourceType());
        return datum;
    }

    /**
     * Search DB for org defined funding types
     */
    @RequestMapping(value = "/orgDefinedSubType/{query}", method = RequestMethod.GET)
    public @ResponseBody List<String> searchOrgDefinedFundingSubTypes(@PathVariable("query") String query,
            @RequestParam(value = "limit") int limit) {
        return profileFundingManager.getIndexedFundingSubTypes(query, limit);
    }

    public Locale getUserLocale() {
        return localeManager.getLocale();
    }

    @RequestMapping(value = "/updateToMaxDisplay.json", method = RequestMethod.GET)
    public @ResponseBody boolean updateToMaxDisplay(HttpServletRequest request,
            @RequestParam(value = "putCode") Long putCode) {
        OrcidProfile profile = getEffectiveProfile();
        return profileFundingManager.updateToMaxDisplay(profile.getOrcidIdentifier().getPath(), putCode);
    }

    /**
     * New external identifiers for funding have relationship field, so, we need to add those new fields to the funding object so they can be persisted.
     * This function might be used between the transition from API 1.2 style API 2.0 style
     * @param profileFunding
     *          The object to be enhanced
     * @param funding
     *          The object containing the external identifiers used to enhance funding object          
     * */
    private void enhanceExternalIdentifiers(ProfileFundingEntity profileFunding, FundingForm funding) {
        if (funding != null && funding.getExternalIdentifiers() != null
                && !funding.getExternalIdentifiers().isEmpty()) {
            org.orcid.jaxb.model.record_rc1.FundingExternalIdentifiers extIds = new org.orcid.jaxb.model.record_rc1.FundingExternalIdentifiers();
            for (FundingExternalIdentifierForm extIdForm : funding.getExternalIdentifiers()) {
                org.orcid.jaxb.model.record_rc1.FundingExternalIdentifier extId = extIdForm
                        .toRecordFundingExternalIdentifier();
                extIds.getExternalIdentifier().add(extId);
            }

            //Transform the message external identifiers to core external identifiers
            FundingExternalIdentifiers feis = FundingExternalIdentifiers.fromRecordPojo(extIds);

            profileFunding.setExternalIdentifiersJson(JsonUtils.convertToJsonString(feis));
        }
    }

    /**
     * New external identifiers for funding have relationship field, so, we need to add those new fields to the funding object so they can be displayed in the UI.
     * This function might be used between the transition from API 1.2 style API 2.0 style
     * @param funding
     *          The object to be enhanced
     * @param extIds
     *          The external identifiers used to enhance funding object          
     * */
    private void enhanceExternalIdentifiers(FundingForm funding,
            org.orcid.jaxb.model.record_rc1.FundingExternalIdentifiers extIds) {
        List<FundingExternalIdentifierForm> fundingExternalIdentifiers = new ArrayList<FundingExternalIdentifierForm>();
        if (extIds != null && !extIds.getExternalIdentifier().isEmpty()) {
            for (FundingExternalIdentifier extId : extIds.getExternalIdentifier()) {
                FundingExternalIdentifierForm newExtId = FundingExternalIdentifierForm.valueOf(extId);
                //Set the relationship default value
                if (PojoUtil.isEmpty(newExtId.getRelationship())) {
                    newExtId.setRelationship(Text.valueOf(Relationship.SELF.value()));
                }
                fundingExternalIdentifiers.add(newExtId);
            }
        }
        funding.setExternalIdentifiers(fundingExternalIdentifiers);
    }

}