gt:         if (!institutionalProposal.getSpecialReviews().isEmpty()) {
context: /*
   * Kuali Coeus, a comprehensive research administration system for higher education.
   * 
   * Copyright 2005-2016 Kuali, Inc.
   * 
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Affero General Public License as
   * published by the Free Software Foundation, either version 3 of the
   * License, or (at your option) any later version.
   * 
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Affero General Public License for more details.
   * 
   * You should have received a copy of the GNU Affero General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package org.kuali.kra.institutionalproposal.service.impl;
  
  import org.apache.commons.lang3.StringUtils;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  import org.kuali.coeus.coi.framework.Project;
  import org.kuali.coeus.coi.framework.ProjectPublisher;
  import org.kuali.coeus.coi.framework.ProjectRetrievalService;
  import org.kuali.coeus.common.framework.custom.attr.CustomAttribute;
  import org.kuali.coeus.common.framework.custom.attr.CustomAttributeDocValue;
  import org.kuali.coeus.common.framework.custom.attr.CustomAttributeDocument;
  import org.kuali.coeus.common.framework.version.VersionException;
  import org.kuali.coeus.common.framework.version.VersionStatus;
  import org.kuali.coeus.common.framework.version.VersioningService;
  import org.kuali.coeus.propdev.impl.core.DevelopmentProposal;
  import org.kuali.coeus.propdev.impl.core.ProposalTypeService;
  import org.kuali.coeus.propdev.impl.person.ProposalPerson;
  import org.kuali.coeus.propdev.impl.person.ProposalPersonUnit;
  import org.kuali.coeus.propdev.impl.person.creditsplit.ProposalPersonCreditSplit;
  import org.kuali.coeus.propdev.impl.person.creditsplit.ProposalUnitCreditSplit;
  import org.kuali.coeus.sys.framework.service.KcServiceLocator;
  import org.kuali.kra.award.home.Award;
  import org.kuali.coeus.propdev.impl.keyword.PropScienceKeyword;
  import org.kuali.coeus.sys.api.model.ScaleTwoDecimal;
  import org.kuali.coeus.common.budget.framework.core.Budget;
  import org.kuali.coeus.common.budget.framework.distribution.BudgetCostShare;
  import org.kuali.coeus.common.budget.framework.distribution.BudgetUnrecoveredFandA;
  import org.kuali.coeus.common.budget.framework.period.BudgetPeriod;
  import org.kuali.kra.infrastructure.Constants;
  import org.kuali.kra.institutionalproposal.ProposalStatus;
  import org.kuali.kra.institutionalproposal.contacts.*;
  import org.kuali.kra.institutionalproposal.customdata.InstitutionalProposalCustomData;
  import org.kuali.kra.institutionalproposal.dao.InstitutionalProposalDao;
  import org.kuali.kra.institutionalproposal.document.InstitutionalProposalDocument;
  import org.kuali.kra.institutionalproposal.exception.InstitutionalProposalCreationException;
  import org.kuali.kra.institutionalproposal.home.InstitutionalProposal;
  import org.kuali.kra.institutionalproposal.home.InstitutionalProposalCostShare;
  import org.kuali.kra.institutionalproposal.home.InstitutionalProposalUnrecoveredFandA;
  import org.kuali.kra.institutionalproposal.proposaladmindetails.ProposalAdminDetails;
  import org.kuali.kra.institutionalproposal.service.InstitutionalProposalService;
  import org.kuali.kra.institutionalproposal.service.InstitutionalProposalVersioningService;
  import org.kuali.kra.institutionalproposal.specialreview.InstitutionalProposalSpecialReview;
  import org.kuali.kra.institutionalproposal.specialreview.InstitutionalProposalSpecialReviewExemption;
  import org.kuali.coeus.propdev.impl.specialreview.ProposalSpecialReview;
  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
  import org.kuali.rice.kew.api.exception.WorkflowException;
  import org.kuali.rice.krad.data.DataObjectService;
  import org.kuali.rice.krad.service.BusinessObjectService;
  import org.kuali.rice.krad.service.DocumentService;
  import org.kuali.rice.krad.service.SequenceAccessorService;
  import org.kuali.rice.krad.util.ObjectUtils;
  import org.springframework.transaction.annotation.Transactional;
  
  import java.io.IOException;
  import java.text.DecimalFormat;
  import java.util.*;
  import java.util.stream.Collectors;
  
  /**
   * This class provides the default implementation of the InstitutionalProposalService.
   */
  @Transactional
  public class InstitutionalProposalServiceImpl implements InstitutionalProposalService {
      private static final Log LOG = LogFactory.getLog(InstitutionalProposalServiceImpl.class);
      
      private static final String WORKFLOW_EXCEPTION_MESSAGE = "Caught workflow exception creating new Institutional Proposal";
      private static final String VERSION_EXCEPTION_MESSAGE = "Caught version exception creating new Institutional Proposal";
      private static final String ROUTE_MESSAGE = "Autogenerated Institutional Proposal from Development Proposal ";
      private static final String NO_PRIOR_VERSION_MESSAGE = "Tried to version an InstitutionalProposal where no prior version exists.";
      private static final String NEW_DOCUMENT_DESCRIPTION = "Generated by Dev Proposal ";
      private static final String DECIMAL_FORMAT = "00000000";
      private static final String PROPOSAL_NUMBER = "proposalNumber";
      private static final String SEQUENCE_NUMBER = "sequenceNumber";
      private static final String ACTIVE_VALUE = "Y";
      private static final String INST_PROPOSAL_ID = "instProposalId";
      private static final int DEFAULT_STATUS_CODE = 1;
      private static final int WITHDRAWN_STATUS_CODE = 5;
      private static final int DEFAULT_COST_SHARE_TYPE_CODE = 1;
      private static final String VALID_FUNDING_PROPOSAL_STATUS_CODES = "validFundingProposalStatusCodes";
      private static final String SEPARATOR = ",";
      private static final String TRUE_INDICATOR_VALUE = "1";
      private static final String FALSE_INDICATOR_VALUE = "0";
  
      private BusinessObjectService businessObjectService;
      private DocumentService documentService;
      private VersioningService versioningService;
      private InstitutionalProposalVersioningService institutionalProposalVersioningService;
      private SequenceAccessorService sequenceAccessorService;
      private ParameterService parameterService;
      private InstitutionalProposalDao institutionalProposalDao;
      private DataObjectService dataObjectService;
  
      private ProjectRetrievalService instPropProjectRetrievalService;
  
      private ProposalTypeService proposalTypeService;
      private ProjectPublisher projectPublisher;
  
      public ProposalTypeService getProposalTypeService() {
          //since PD is loaded after IP and @Lazy does not work, we have to use the ServiceLocator
          if (proposalTypeService == null) {
              proposalTypeService = KcServiceLocator.getService(ProposalTypeService.class);
          }
  
  
          return proposalTypeService;
      }
  
      public ProjectPublisher getProjectPublisher() {
          //since COI is loaded last and @Lazy does not work, we have to use the ServiceLocator
          if (projectPublisher == null) {
              projectPublisher = KcServiceLocator.getService(ProjectPublisher.class);
          }
  
          return projectPublisher;
      }
  
      
      /**
       * Creates a new pending Institutional Proposal based on given development proposal and budget.
       *
       * @param developmentProposal DevelopmentProposal
       * @param budget Budget
       * @return String The new institutional proposal
       * @see org.kuali.kra.institutionalproposal.service.InstitutionalProposalService#createInstitutionalProposal(DevelopmentProposal, Budget)
       */
      public InstitutionalProposal createInstitutionalProposal(DevelopmentProposal developmentProposal, Budget budget) {
          
          try {
              InstitutionalProposal institutionalProposal = new InstitutionalProposal();
              
              // Set proposal number on new Institutional Proposal so that it will be propagated to all created child BO's before initial save.
  
              institutionalProposal.setProposalNumber(getNextInstitutionalProposalNumber());
              
              InstitutionalProposalDocument institutionalProposalDocument = mergeProposals(institutionalProposal, developmentProposal, budget);
              setInstitutionalProposalIndicators(institutionalProposalDocument.getInstitutionalProposal());
              documentService.routeDocument(institutionalProposalDocument, ROUTE_MESSAGE + developmentProposal.getProposalNumber(), new ArrayList<>());
              final Project project = getInstPropProjectRetrievalService().retrieveProject(institutionalProposalDocument.getInstitutionalProposal().getProposalNumber());
              if (project != null) {
                  getProjectPublisher().publishProject(project);
              }
              return institutionalProposalDocument.getInstitutionalProposal();
          } catch (WorkflowException ex) {
              throw new InstitutionalProposalCreationException(WORKFLOW_EXCEPTION_MESSAGE, ex);
          } 
      }
      
      /**
       * Creates a new active version of the Institutional Proposal corresponding to the given proposal number, 
       * with data copied from the given development proposal and budget.
       * 
       * @param proposalNumber String
       * @param developmentProposal DevelopmentProposal
       * @param budget Budget
       * @return String The new version number
       * @see org.kuali.kra.institutionalproposal.service.InstitutionalProposalService#createInstitutionalProposalVersion(String, DevelopmentProposal, Budget)
       */
      public InstitutionalProposal createInstitutionalProposalVersion(String proposalNumber, DevelopmentProposal developmentProposal, Budget budget) {
          
          try {
              InstitutionalProposalDocument newInstitutionalProposalDocument = versionProposal(proposalNumber, developmentProposal, budget);
              setInstitutionalProposalIndicators(newInstitutionalProposalDocument.getInstitutionalProposal());
              documentService.routeDocument(newInstitutionalProposalDocument, ROUTE_MESSAGE + developmentProposal.getProposalNumber(),
                      new ArrayList<>());
              institutionalProposalVersioningService.updateInstitutionalProposalVersionStatus(newInstitutionalProposalDocument.getInstitutionalProposal(),
                      VersionStatus.ACTIVE);
              final Project project = getInstPropProjectRetrievalService().retrieveProject(newInstitutionalProposalDocument.getInstitutionalProposal().getProposalNumber());
              if (project != null) {
                  getProjectPublisher().publishProject(project);
              }
              return newInstitutionalProposalDocument.getInstitutionalProposal();
          } catch (WorkflowException|VersionException e) {
              throw new InstitutionalProposalCreationException(VERSION_EXCEPTION_MESSAGE, e);
          }
      }
      
      /**
       * Return an Institutional Proposal, if one exists.
       * 
       * @param proposalId String
       * @return InstitutionalProposal, or null if none is found.
       * @see org.kuali.kra.institutionalproposal.service.InstitutionalProposalService#getInstitutionalProposal(String)
       */
      public InstitutionalProposal getInstitutionalProposal(String proposalId) {
          Map<String, String> criteria = new HashMap<>();
          criteria.put(InstitutionalProposal.PROPOSAL_ID_PROPERTY_STRING, proposalId);
          return  businessObjectService.findByPrimaryKey(InstitutionalProposal.class, criteria);
      }
      
      /**
       * Return the PENDING version of an Institutional Proposal, if one exists.
       * Note, PENDING here refers to the Version Status, NOT the Proposal Status of the Institutional Proposal.
       * 
       * This is just a pass-through to InstitutionalProposalVersioningService, but we needed this method to be part of 
       * the module API.
       * 
       * @param proposalNumber String
       * @return InstitutionalProposal, or null if a PENDING version is not found.
       * @see org.kuali.coeus.common.framework.version.VersionStatus
       */
      public InstitutionalProposal getPendingInstitutionalProposalVersion(String proposalNumber) {
          return institutionalProposalVersioningService.getPendingInstitutionalProposalVersion(proposalNumber);
      }
      
      /**
       * Return the ACTIVE version of an Institutional Proposal, if one exists.
       * Note, ACTIVE here refers to the Version Status, NOT the Proposal Status of the Institutional Proposal.
       * 
       * @param proposalNumber String
       * @return InstitutionalProposal, or null if a ACTIVE version is not found.
       * @see org.kuali.coeus.common.framework.version.VersionStatus
       */
      public InstitutionalProposal getActiveInstitutionalProposalVersion(String proposalNumber) {
          return institutionalProposalVersioningService.getActiveInstitutionalProposalVersion(proposalNumber);
      }
      
      /**
       * Designate one or more Institutional Proposals as Funded by an Award.
       * 
       * If the given Proposal has a Proposal Status of Pending, a new Final version of the Proposal
       * will be created with a Proposal Status of Funded.
       * 
       * If the current Active version is already Funded, it will be left alone.
       * 
       * @param proposalNumbers The proposals to update.
       * @return List&lt;InstitutionalProposal&gt; The new Funded versions.
       */
      public List<InstitutionalProposal> fundInstitutionalProposals(Set<String> proposalNumbers) {
  
          List<InstitutionalProposal> updatedProposals = new ArrayList<>();
          
          try {
              for (String proposalNumber : proposalNumbers) {
                  InstitutionalProposal activeVersion = getActiveInstitutionalProposal(proposalNumber);
                  
                  if (activeVersion != null && !ProposalStatus.FUNDED.equals(activeVersion.getStatusCode())) {
                      
                      LOG.info("Creating a new version of proposal " + proposalNumber + ".");
                      
                      InstitutionalProposal newVersion = versioningService.createNewVersion(activeVersion);
                      newVersion.setStatusCode(ProposalStatus.FUNDED);
                      newVersion.setAwardFundingProposals(new ArrayList<>());
                      
                      InstitutionalProposalDocument institutionalProposalDocument = 
                          (InstitutionalProposalDocument) documentService.getNewDocument(InstitutionalProposalDocument.class);
                      
                      institutionalProposalDocument.getDocumentHeader().setDocumentDescription(
                              activeVersion.getInstitutionalProposalDocument().getDocumentHeader().getDocumentDescription());
                      
                      institutionalProposalDocument.setInstitutionalProposal(newVersion);
                      setInstitutionalProposalIndicators(institutionalProposalDocument.getInstitutionalProposal());
                      documentService.routeDocument(institutionalProposalDocument, 
                              "Update Proposal Status to Funded", new ArrayList<>());
                      
                      updatedProposals.add(newVersion);
                      
                  } else if (activeVersion != null && ProposalStatus.FUNDED.equals(activeVersion.getStatusCode())) {
                      LOG.info("Skipped creating a new version of proposal " + proposalNumber + " - proposal is already Funded.");
                  } else if (activeVersion == null) {
                      LOG.warn("Could not designate proposal " + proposalNumber + " as Funded: no Active version found.");
                  }
              }
              
              return updatedProposals;
              
          } catch (WorkflowException we) {
              throw new InstitutionalProposalCreationException(WORKFLOW_EXCEPTION_MESSAGE, we);
          } catch (VersionException ve) {
              throw new InstitutionalProposalCreationException(VERSION_EXCEPTION_MESSAGE, ve);
          }
      }
      
      /**
       * Designate the given Proposals as no longer funded by the given Award.
       * 
       * If the given Award was the only funding Award for a Proposal, a new Final version of the Proposal
       * will be created with a Proposal Status of Pending.
       * 
       * If the Proposal has other funding Awards, it will be left alone.  It will also be left alone
       * if it is funded by the active version of the given award number (this is a functional requirement).
       * 
       * @param proposalNumbers The proposals to update.
       * @param awardNumber The Award that is de-funding the proposal.
       * @param awardSequence The sequence number of the Award.
       * @return List&lt;InstitutionalProposal&gt; The new Pending versions.
       */
      public List<InstitutionalProposal> defundInstitutionalProposals(Set<String> proposalNumbers, String awardNumber, Integer awardSequence) {
  
          List<InstitutionalProposal> updatedProposals = new ArrayList<>();
          
          try {
              for (String proposalNumber : proposalNumbers) {
                  InstitutionalProposal activeVersion = getActiveInstitutionalProposal(proposalNumber);
                  
                  if (activeVersion != null && activeVersion.isFundedByAward(awardNumber, awardSequence)
                          && activeVersion.getActiveAwardFundingProposals().size() == 1) {
                      LOG.info("Creating a new version of proposal " + proposalNumber + ".");
                      
                      InstitutionalProposal newVersion = versioningService.createNewVersion(activeVersion);
                      newVersion.getAwardFundingProposals().clear();
                      newVersion.setStatusCode(ProposalStatus.PENDING);
                      
                      InstitutionalProposalDocument institutionalProposalDocument = 
                          (InstitutionalProposalDocument) documentService.getNewDocument(InstitutionalProposalDocument.class);
                      
                      institutionalProposalDocument.getDocumentHeader().setDocumentDescription(
                              activeVersion.getInstitutionalProposalDocument().getDocumentHeader().getDocumentDescription());
                      
                      institutionalProposalDocument.setInstitutionalProposal(newVersion);
                      setInstitutionalProposalIndicators(institutionalProposalDocument.getInstitutionalProposal());
                      documentService.routeDocument(institutionalProposalDocument, 
                              "Update Proposal Status to Pending", new ArrayList<>());
                      
                      updatedProposals.add(newVersion);
                      
                  } else {
                      LOG.info("Skipped setting proposal " + proposalNumber + " to Pending. It is either funded by another Award, " 
                              + "another version of Award " + awardNumber + ", or no active version found.");
                  }
              }
              
              return updatedProposals;
              
          } catch (WorkflowException we) {
              throw new InstitutionalProposalCreationException(WORKFLOW_EXCEPTION_MESSAGE, we);
          } catch (VersionException ve) {
              throw new InstitutionalProposalCreationException(VERSION_EXCEPTION_MESSAGE, ve);
          }
      }
  
      public List<InstitutionalProposal> getProposalsForProposalNumber(String proposalNumber) {
          return new ArrayList<>(businessObjectService.findMatchingOrderBy(InstitutionalProposal.class,
                  Collections.singletonMap(PROPOSAL_NUMBER, proposalNumber),
                                                                  SEQUENCE_NUMBER,
                                                                  true));
      }
      
      @Override
      public List<DevelopmentProposal> getAllLinkedDevelopmentProposals(String proposalNumber) {
          List<DevelopmentProposal> result = new ArrayList<>();
          List<InstitutionalProposal> proposals = getProposalsForProposalNumber(proposalNumber);
          for (InstitutionalProposal curProposal : proposals) {
              List<ProposalAdminDetails> details = new ArrayList<>(businessObjectService.findMatching(ProposalAdminDetails.class,
                      Collections.singletonMap(INST_PROPOSAL_ID, curProposal.getProposalId())));
              for (ProposalAdminDetails detail : details) {
              	result.add(dataObjectService.find(DevelopmentProposal.class, detail.getDevProposalNumber()));
              }
          }
          return result;
      }
      
      public String getNextInstitutionalProposalNumber() {
          Long nextProposalNumber = sequenceAccessorService.getNextAvailableSequenceNumber(Constants.INSTITUTIONAL_PROPSAL_PROPSAL_NUMBER_SEQUENCE, InstitutionalProposal.class);
          DecimalFormat formatter = new DecimalFormat(DECIMAL_FORMAT);
          return formatter.format(nextProposalNumber);
      }
  
      protected InstitutionalProposal getActiveInstitutionalProposal(String proposalNumber) {
          Map<String, String> criteria = new HashMap<>();
          criteria.put(InstitutionalProposal.PROPOSAL_NUMBER_PROPERTY_STRING, proposalNumber);
          criteria.put(InstitutionalProposal.PROPOSAL_SEQUENCE_STATUS_PROPERTY_STRING, VersionStatus.ACTIVE.toString());
          Collection results = businessObjectService.findMatching(InstitutionalProposal.class, criteria);
          if (results.isEmpty()) {
              return null;
          }
          
          return (InstitutionalProposal) results.toArray()[0];
      }
      
      protected InstitutionalProposalDocument versionProposal(String proposalNumber, DevelopmentProposal developmentProposal, Budget budget)
          throws VersionException, WorkflowException {
          
          InstitutionalProposal currentVersion = getActiveInstitutionalProposal(proposalNumber);
          if (currentVersion == null) {
              throw new RuntimeException(NO_PRIOR_VERSION_MESSAGE);
          }
          ObjectUtils.materializeObjects(currentVersion.getInstitutionalProposalScienceKeywords());
          InstitutionalProposal newVersion = versioningService.createNewVersion(currentVersion);
          return mergeProposals(newVersion, developmentProposal, budget);
      }
      
      protected InstitutionalProposalDocument mergeProposals(InstitutionalProposal institutionalProposal, DevelopmentProposal developmentProposal, Budget budget)
          throws WorkflowException {
          
          InstitutionalProposalDocument institutionalProposalDocument = 
              (InstitutionalProposalDocument) documentService.getNewDocument(InstitutionalProposalDocument.class);
          
          institutionalProposalDocument.getDocumentHeader().setDocumentDescription(
                  NEW_DOCUMENT_DESCRIPTION + developmentProposal.getProposalNumber());
          
          institutionalProposalDocument.setInstitutionalProposal(institutionalProposal);
  
          doBaseFieldsDataFeed(institutionalProposal, developmentProposal);
          doCustomAttributeDataFeed(institutionalProposalDocument, developmentProposal);
          
          institutionalProposal.getProjectPersons().clear();
          for (ProposalPerson pdPerson : developmentProposal.getProposalPersons()) {
              institutionalProposal.add(generateInstitutionalProposalPerson(pdPerson));
          }
          
          institutionalProposal.getSpecialReviews().clear();
          for (ProposalSpecialReview dpSpecialReview : developmentProposal.getPropSpecialReviews()) {
              institutionalProposal.addSpecialReview(generateIpSpecialReview(dpSpecialReview));
          }
  