<commit_before>/*
 * Copyright 2008 The Kuali Foundation.
 * 
 * Licensed under the Educational Community License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.opensource.org/licenses/ecl1.php
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.kra.s2s.service.impl;

import gov.grants.apply.system.globalV10.HashValueDocument.HashValue;
import gov.grants.apply.system.headerV10.GrantSubmissionHeaderDocument.GrantSubmissionHeader;
import gov.grants.apply.system.metaGrantApplication.GrantApplicationDocument;
import gov.grants.apply.system.metaGrantApplication.GrantApplicationDocument.GrantApplication;
import gov.grants.apply.system.metaGrantApplication.GrantApplicationDocument.GrantApplication.Forms;
import gov.grants.apply.webservices.applicantintegrationservices_v1.ApplicationInformationType;
import gov.grants.apply.webservices.applicantintegrationservices_v1.GetApplicationListResponse;
import gov.grants.apply.webservices.applicantintegrationservices_v1.GetApplicationStatusDetailResponse;
import gov.grants.apply.webservices.applicantintegrationservices_v1.GetOpportunityListResponse;
import gov.grants.apply.webservices.applicantintegrationservices_v1.OpportunityInformationType;
import gov.grants.apply.webservices.applicantintegrationservices_v1.SubmitApplicationResponse;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.activation.DataHandler;
import javax.mail.util.ByteArrayDataSource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.kuali.kra.infrastructure.Constants;
import org.kuali.kra.proposaldevelopment.bo.AttachmentDataSource;
import org.kuali.kra.proposaldevelopment.bo.DevelopmentProposal;
import org.kuali.kra.proposaldevelopment.document.ProposalDevelopmentDocument;
import org.kuali.kra.s2s.S2SException;
import org.kuali.kra.s2s.bo.S2sAppAttachments;
import org.kuali.kra.s2s.bo.S2sAppSubmission;
import org.kuali.kra.s2s.bo.S2sApplication;
import org.kuali.kra.s2s.bo.S2sOppForms;
import org.kuali.kra.s2s.bo.S2sOpportunity;
import org.kuali.kra.s2s.formmapping.FormMappingInfo;
import org.kuali.kra.s2s.formmapping.FormMappingLoader;
import org.kuali.kra.s2s.generator.S2SFormGenerator;
import org.kuali.kra.s2s.generator.S2SGeneratorNotFoundException;
import org.kuali.kra.s2s.generator.bo.AttachmentData;
import org.kuali.kra.s2s.service.GrantsGovConnectorService;
import org.kuali.kra.s2s.service.PrintService;
import org.kuali.kra.s2s.service.S2SFormGeneratorService;
import org.kuali.kra.s2s.service.S2SProposalValidatorService;
import org.kuali.kra.s2s.service.S2SService;
import org.kuali.kra.s2s.service.S2SUtilService;
import org.kuali.kra.s2s.service.S2SValidatorService;
import org.kuali.kra.s2s.util.GrantApplicationHash;
import org.kuali.kra.s2s.util.S2SConstants;
import org.kuali.kra.s2s.validator.OpportunitySchemaParser;
import org.kuali.rice.kns.bo.PersistableBusinessObject;
import org.kuali.rice.kns.service.BusinessObjectService;
import org.kuali.rice.kns.service.DateTimeService;
import org.kuali.rice.kns.util.AuditCluster;
import org.kuali.rice.kns.util.AuditError;
import org.kuali.rice.kns.util.GlobalVariables;

/**
 * 
 * This class is implementation of S2SService
 * 
 * @author Kuali Research Administration Team
 */
public class KRAS2SServiceImpl implements S2SService {
	private static final Logger LOG = Logger.getLogger(KRAS2SServiceImpl.class);
	private BusinessObjectService businessObjectService;
	private DateTimeService dateTimeService;
	private S2SFormGeneratorService s2SFormGeneratorService;
	private S2SProposalValidatorService s2SProposalValidatorService;
	private S2SUtilService s2SUtilService;
	private PrintService printService;
	private GrantsGovConnectorService grantsGovConnectorService;
	private S2SValidatorService s2SValidatorService;
	private static final String GRANTS_GOV_STATUS_ERROR = "ERROR";
	private static final String KEY_PROPOSAL_NUMBER = "proposalNumber";

	/**
	 * 
	 * This method is used to get the application status details.
	 * 
	 * @param ggTrackingId
	 *            grants gov tracking id for the application.
	 * @param proposalNumber
	 *            Proposal number.
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#getStatusDetails(java.lang.String,
	 *      java.lang.String)
	 */
	public Object getStatusDetails(String ggTrackingId, String proposalNumber)
			throws S2SException {
		Object statusDetail = null;
		GetApplicationStatusDetailResponse applicationStatusDetailResponse;
		applicationStatusDetailResponse = grantsGovConnectorService
				.getApplicationStatusDetail(ggTrackingId, proposalNumber);
		if (applicationStatusDetailResponse != null) {
			statusDetail = applicationStatusDetailResponse.getDetailedStatus();
		}
		return statusDetail;
	}

	/**
	 * 
	 * This method returns the list of forms for a given opportunity
	 * 
	 * @param opportunity
	 * @return {@link List} of {@link S2sOppForms} which are included in the
	 *         given {@link S2sOpportunity}
	 * @see org.kuali.kra.s2s.service.S2SService#parseOpportunityForms(org.kuali.kra.s2s.bo.S2sOpportunity)
	 */
	public List<S2sOppForms> parseOpportunityForms(S2sOpportunity opportunity) {
		return new OpportunitySchemaParser().getForms(opportunity
				.getSchemaUrl());
	}

	/**
	 * This method checks for the status of submission for the given
	 * {@link ProposalDevelopmentDocument} on Grants.gov
	 * 
	 * @param pdDoc
	 *            for which status has to be checked
	 * @return boolean, <code>true</code> if status has changed, false
	 *         otherwise
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#refreshGrantsGov(org.kuali.kra.proposaldevelopment.document.ProposalDevelopmentDocument)
	 */
	public boolean refreshGrantsGov(ProposalDevelopmentDocument pdDoc)
			throws S2SException {
		GetApplicationListResponse applicationListResponse = fetchApplicationListResponse(pdDoc);
		if (applicationListResponse != null) {
			saveGrantsGovStatus(pdDoc, applicationListResponse);
		}
		return true;
	}

	/**
	 * This method fetches the application list from Grants.gov for a given
	 * proposal
	 * 
	 * @param pdDoc
	 * @return {@link GetApplicationListResponse}
	 * @throws S2SException
	 */
	public GetApplicationListResponse fetchApplicationListResponse(
			ProposalDevelopmentDocument pdDoc) throws S2SException {
		Map<String, String> opportunityMap = new HashMap<String, String>();
		opportunityMap.put(KEY_PROPOSAL_NUMBER, pdDoc.getDevelopmentProposal()
				.getProposalNumber());
		S2sOpportunity s2sOpportunity = (S2sOpportunity) businessObjectService
				.findByPrimaryKey(S2sOpportunity.class, opportunityMap);
		GetApplicationListResponse applicationListResponse = grantsGovConnectorService
				.getApplicationList(s2sOpportunity.getOpportunityId(),
						s2sOpportunity.getCfdaNumber(), s2sOpportunity
								.getProposalNumber());
		return applicationListResponse;
	}

	/**
	 * 
	 * This method fetches the status from Grants Gov and saves in case status
	 * is modified
	 * 
	 * @param pdDoc
	 *            {@link ProposalDevelopmentDocument}
	 * @param appSubmission
	 *            {@link S2sAppSubmission}
	 * @param applicationListResponse
	 *            {@link GetApplicationListResponse} response from Grants Gov
	 * @throws S2SException
	 */
	private void saveGrantsGovStatus(ProposalDevelopmentDocument pdDoc,
			GetApplicationListResponse applicationListResponse)
			throws S2SException {
		S2sAppSubmission appSubmission = null;
		List<S2sAppSubmission> appSubmissionList = pdDoc
				.getDevelopmentProposal().getS2sAppSubmission();
		int submissionNo = 0;
		for (S2sAppSubmission s2AppSubmission : appSubmissionList) {
			if (s2AppSubmission.getSubmissionNumber() > submissionNo) {
				appSubmission = s2AppSubmission;
				submissionNo = s2AppSubmission.getSubmissionNumber();
			}
		}

		if (appSubmission != null) {
			boolean statusChanged = false;
			if (applicationListResponse.getApplicationInformation() == null
					|| applicationListResponse.getApplicationInformation()
							.size() == 0) {
				statusChanged = checkForSubmissionStatusChange(pdDoc,
						appSubmission);
			} else {
				int appSize = applicationListResponse
						.getApplicationInformation().size();
				ApplicationInformationType ggApplication = applicationListResponse
						.getApplicationInformation().get(appSize - 1);
				if (ggApplication != null) {
					statusChanged = !appSubmission.getStatus()
							.equalsIgnoreCase(
									ggApplication
											.getGrantsGovApplicationStatus()
											.value());
					populateAppSubmission(appSubmission, ggApplication);
                    if (StringUtils.isNotBlank(appSubmission.getAgencyTrackingId())) {
                        pdDoc.getDevelopmentProposal().setSponsorProposalNumber(appSubmission.getAgencyTrackingId());
                    }					
				}
			}
			if (statusChanged) {
				businessObjectService.save(appSubmission);
			}
		}

	}

	/**
	 * This method checks if status on grants.gov has changed since last check
	 * and returns the status.
	 * 
	 * @param pdDoc
	 * @param appSubmission
	 * @return status
	 * @throws S2SException
	 */
	public boolean checkForSubmissionStatusChange(
			ProposalDevelopmentDocument pdDoc, S2sAppSubmission appSubmission)
			throws S2SException {
		boolean statusChanged = false;
		GetApplicationStatusDetailResponse applicationStatusDetailResponse = grantsGovConnectorService
				.getApplicationStatusDetail(appSubmission.getGgTrackingId(),
						pdDoc.getDevelopmentProposal().getProposalNumber());
		if (applicationStatusDetailResponse != null
				&& applicationStatusDetailResponse.getDetailedStatus() != null) {
			String statusDetail = applicationStatusDetailResponse
					.getDetailedStatus().toString();
			String statusStr = statusDetail.toUpperCase().indexOf(
					GRANTS_GOV_STATUS_ERROR) == -1 ? statusDetail
					: S2SConstants.STATUS_GRANTS_GOV_SUBMISSION_ERROR;
			statusChanged = !appSubmission.getStatus().equalsIgnoreCase(
					statusStr);
			appSubmission.setComments(statusDetail);
			appSubmission.setStatus(statusStr);
		} else {
			appSubmission
					.setComments(S2SConstants.STATUS_NO_RESPONSE_FROM_GRANTS_GOV);
		}
		return statusChanged;
	}

	/**
	 * This method populates the {@link S2sAppSubmission} BO with details from
	 * {@link ApplicationInformationType}
	 * 
	 * @param appSubmission
	 * @param ggApplication
	 */
	public void populateAppSubmission(S2sAppSubmission appSubmission,
			ApplicationInformationType ggApplication) {
		appSubmission.setGgTrackingId(ggApplication
				.getGrantsGovTrackingNumber());
		appSubmission
				.setReceivedDate(new Timestamp(ggApplication
						.getReceivedDateTime().toGregorianCalendar()
						.getTimeInMillis()));
		appSubmission.setStatus(ggApplication.getGrantsGovApplicationStatus()
				.toString());
		appSubmission.setLastModifiedDate(new Timestamp(ggApplication
				.getStatusDateTime().toGregorianCalendar().getTimeInMillis()));
		appSubmission.setAgencyTrackingId(ggApplication
				.getAgencyTrackingNumber());
		if (ggApplication.getAgencyTrackingNumber() != null
				&& ggApplication.getAgencyTrackingNumber().length() > 0) {
			appSubmission
					.setComments(S2SConstants.STATUS_AGENCY_TRACKING_NUMBER_ASSIGNED);
		} else {
			appSubmission.setComments(ggApplication
					.getGrantsGovApplicationStatus().toString());
		}
	}

	/**
	 * This method is to find the list of available opportunities for a given
	 * cfda number, opportunity ID and competition ID.
	 * 
	 * @param cfdaNumber
	 *            of the opportunity.
	 * @param opportunityId
	 *            parameter for the opportunity.
	 * @param competitionId
	 *            parameter for the opportunity.
	 * @return List<S2sOpportunity> a list containing the available
	 *         opportunities for the corresponding parameters.
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#searchOpportunity(java.lang.String,
	 *      java.lang.String, java.lang.String)
	 */
	public List<S2sOpportunity> searchOpportunity(String cfdaNumber,
			String opportunityId, String competitionId) throws S2SException {
		List<S2sOpportunity> s2sOpportunityList;
		s2sOpportunityList = convertToArrayList(grantsGovConnectorService
				.getOpportunityList(cfdaNumber, opportunityId, competitionId));
		return s2sOpportunityList;
	}

	/**
	 * This method is used to submit forms to Grants.gov. It generates forms for
	 * a given {@link ProposalDevelopmentDocument}, validates and then submits
	 * the forms
	 * 
	 * @param pdDoc
	 *            Proposal Development Document.
	 * @return true if submitted false otherwise.
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#submitApplication(org.kuali.kra.proposaldevelopment.document.ProposalDevelopmentDocument)
	 */
	public boolean submitApplication(ProposalDevelopmentDocument pdDoc)
			throws S2SException {
		boolean submissionStatus = false;
		Forms forms = Forms.Factory.newInstance();
		List<AttachmentData> attList = new ArrayList<AttachmentData>();
		if (generateAndValidateForms(forms, attList, pdDoc)) {
			GrantApplicationDocument grantApplicationDocument = getGrantApplicationDocument(
					pdDoc, forms);

			Map<String, DataHandler> attachments = new HashMap<String, DataHandler>();
			List<S2sAppAttachments> s2sAppAttachmentList = new ArrayList<S2sAppAttachments>();
			DataHandler attachmentFile;
			for (AttachmentData attachmentData : attList) {
				attachmentFile = new DataHandler(new ByteArrayDataSource(
						attachmentData.getContent(), attachmentData
								.getContentType()));

				attachments.put(attachmentData.getContentId(), attachmentFile);
				S2sAppAttachments appAttachments = new S2sAppAttachments();
				appAttachments.setContentId(attachmentData.getContentId());
				appAttachments.setProposalNumber(pdDoc.getDevelopmentProposal()
						.getProposalNumber());
				s2sAppAttachmentList.add(appAttachments);
			}
			S2sAppSubmission appSubmission = new S2sAppSubmission();
			appSubmission.setStatus(S2SConstants.GRANTS_GOV_STATUS_MESSAGE);
			appSubmission.setComments(S2SConstants.GRANTS_GOV_COMMENTS_MESSAGE);
			SubmitApplicationResponse response = null;
			XmlOptions xmlOptions = new XmlOptions();
			Map<String, String> prefixMap = new HashMap<String, String>();
			prefixMap.put(
					"http://apply.grants.gov/system/MetaGrantApplication", "");
			prefixMap.put(
					"http://apply.grants.gov/forms/RR_SubawardBudget-V1.0",
					"RR_SubawardBudget");
			prefixMap.put(
					"http://apply.grants.gov/forms/RR_SubawardBudget-V1.1",
					"RR_SubawardBudget");
			prefixMap.put(
					"http://apply.grants.gov/forms/RR_SubawardBudget-V1.2",
					"RR_SubawardBudget");

			xmlOptions.setSaveSuggestedPrefixes(prefixMap);

			String applicationXml = grantApplicationDocument
					.xmlText(xmlOptions);
			try {
				response = grantsGovConnectorService.submitApplication(
						applicationXml, attachments, pdDoc
								.getDevelopmentProposal().getProposalNumber());
			} catch (S2SException ex) {
				appSubmission
						.setStatus(S2SConstants.STATUS_GRANTS_GOV_SUBMISSION_ERROR);
				StringBuilder errMsg = new StringBuilder();
				errMsg.append(S2SConstants.GRANTS_GOV_SUBMISION_ERROR_MESSAGE);
				errMsg.append(ex.getMessage());
				appSubmission.setComments(errMsg.toString());
				businessObjectService.save(appSubmission);
				throw ex;
			}
			saveSubmissionDetails(pdDoc, appSubmission, response,
					applicationXml, s2sAppAttachmentList);
			submissionStatus = true;<commit_msg>Jira# KRACOEUS-3272<commit_after>/*
 * Copyright 2008 The Kuali Foundation.
 * 
 * Licensed under the Educational Community License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.opensource.org/licenses/ecl1.php
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.kra.s2s.service.impl;

import gov.grants.apply.system.globalV10.HashValueDocument.HashValue;
import gov.grants.apply.system.headerV10.GrantSubmissionHeaderDocument.GrantSubmissionHeader;
import gov.grants.apply.system.metaGrantApplication.GrantApplicationDocument;
import gov.grants.apply.system.metaGrantApplication.GrantApplicationDocument.GrantApplication;
import gov.grants.apply.system.metaGrantApplication.GrantApplicationDocument.GrantApplication.Forms;
import gov.grants.apply.webservices.applicantintegrationservices_v1.ApplicationInformationType;
import gov.grants.apply.webservices.applicantintegrationservices_v1.GetApplicationListResponse;
import gov.grants.apply.webservices.applicantintegrationservices_v1.GetApplicationStatusDetailResponse;
import gov.grants.apply.webservices.applicantintegrationservices_v1.GetOpportunityListResponse;
import gov.grants.apply.webservices.applicantintegrationservices_v1.OpportunityInformationType;
import gov.grants.apply.webservices.applicantintegrationservices_v1.SubmitApplicationResponse;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.activation.DataHandler;
import javax.mail.util.ByteArrayDataSource;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.kuali.kra.infrastructure.Constants;
import org.kuali.kra.proposaldevelopment.bo.AttachmentDataSource;
import org.kuali.kra.proposaldevelopment.bo.DevelopmentProposal;
import org.kuali.kra.proposaldevelopment.document.ProposalDevelopmentDocument;
import org.kuali.kra.s2s.S2SException;
import org.kuali.kra.s2s.bo.S2sAppAttachments;
import org.kuali.kra.s2s.bo.S2sAppSubmission;
import org.kuali.kra.s2s.bo.S2sApplication;
import org.kuali.kra.s2s.bo.S2sOppForms;
import org.kuali.kra.s2s.bo.S2sOpportunity;
import org.kuali.kra.s2s.formmapping.FormMappingInfo;
import org.kuali.kra.s2s.formmapping.FormMappingLoader;
import org.kuali.kra.s2s.generator.S2SFormGenerator;
import org.kuali.kra.s2s.generator.S2SGeneratorNotFoundException;
import org.kuali.kra.s2s.generator.bo.AttachmentData;
import org.kuali.kra.s2s.service.GrantsGovConnectorService;
import org.kuali.kra.s2s.service.PrintService;
import org.kuali.kra.s2s.service.S2SFormGeneratorService;
import org.kuali.kra.s2s.service.S2SProposalValidatorService;
import org.kuali.kra.s2s.service.S2SService;
import org.kuali.kra.s2s.service.S2SUtilService;
import org.kuali.kra.s2s.service.S2SValidatorService;
import org.kuali.kra.s2s.util.GrantApplicationHash;
import org.kuali.kra.s2s.util.S2SConstants;
import org.kuali.kra.s2s.validator.OpportunitySchemaParser;
import org.kuali.rice.kns.bo.PersistableBusinessObject;
import org.kuali.rice.kns.service.BusinessObjectService;
import org.kuali.rice.kns.service.DateTimeService;
import org.kuali.rice.kns.util.AuditCluster;
import org.kuali.rice.kns.util.AuditError;
import org.kuali.rice.kns.util.GlobalVariables;

/**
 * 
 * This class is implementation of S2SService
 * 
 * @author Kuali Research Administration Team
 */
public class KRAS2SServiceImpl implements S2SService {
	private static final Logger LOG = Logger.getLogger(KRAS2SServiceImpl.class);
	private BusinessObjectService businessObjectService;
	private DateTimeService dateTimeService;
	private S2SFormGeneratorService s2SFormGeneratorService;
	private S2SProposalValidatorService s2SProposalValidatorService;
	private S2SUtilService s2SUtilService;
	private PrintService printService;
	private GrantsGovConnectorService grantsGovConnectorService;
	private S2SValidatorService s2SValidatorService;
	private static final String GRANTS_GOV_STATUS_ERROR = "ERROR";
	private static final String KEY_PROPOSAL_NUMBER = "proposalNumber";

	/**
	 * 
	 * This method is used to get the application status details.
	 * 
	 * @param ggTrackingId
	 *            grants gov tracking id for the application.
	 * @param proposalNumber
	 *            Proposal number.
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#getStatusDetails(java.lang.String,
	 *      java.lang.String)
	 */
	public Object getStatusDetails(String ggTrackingId, String proposalNumber)
			throws S2SException {
		Object statusDetail = null;
		GetApplicationStatusDetailResponse applicationStatusDetailResponse;
		applicationStatusDetailResponse = grantsGovConnectorService
				.getApplicationStatusDetail(ggTrackingId, proposalNumber);
		if (applicationStatusDetailResponse != null) {
			statusDetail = applicationStatusDetailResponse.getDetailedStatus();
		}
		return statusDetail;
	}

	/**
	 * 
	 * This method returns the list of forms for a given opportunity
	 * 
	 * @param opportunity
	 * @return {@link List} of {@link S2sOppForms} which are included in the
	 *         given {@link S2sOpportunity}
	 * @see org.kuali.kra.s2s.service.S2SService#parseOpportunityForms(org.kuali.kra.s2s.bo.S2sOpportunity)
	 */
	public List<S2sOppForms> parseOpportunityForms(S2sOpportunity opportunity) {
		return new OpportunitySchemaParser().getForms(opportunity
				.getSchemaUrl());
	}

	/**
	 * This method checks for the status of submission for the given
	 * {@link ProposalDevelopmentDocument} on Grants.gov
	 * 
	 * @param pdDoc
	 *            for which status has to be checked
	 * @return boolean, <code>true</code> if status has changed, false
	 *         otherwise
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#refreshGrantsGov(org.kuali.kra.proposaldevelopment.document.ProposalDevelopmentDocument)
	 */
	public boolean refreshGrantsGov(ProposalDevelopmentDocument pdDoc)
			throws S2SException {
		GetApplicationListResponse applicationListResponse = fetchApplicationListResponse(pdDoc);
		if (applicationListResponse != null) {
			saveGrantsGovStatus(pdDoc, applicationListResponse);
		}
		return true;
	}

	/**
	 * This method fetches the application list from Grants.gov for a given
	 * proposal
	 * 
	 * @param pdDoc
	 * @return {@link GetApplicationListResponse}
	 * @throws S2SException
	 */
	public GetApplicationListResponse fetchApplicationListResponse(
			ProposalDevelopmentDocument pdDoc) throws S2SException {
		Map<String, String> opportunityMap = new HashMap<String, String>();
		opportunityMap.put(KEY_PROPOSAL_NUMBER, pdDoc.getDevelopmentProposal()
				.getProposalNumber());
		S2sOpportunity s2sOpportunity = (S2sOpportunity) businessObjectService
				.findByPrimaryKey(S2sOpportunity.class, opportunityMap);
		GetApplicationListResponse applicationListResponse = grantsGovConnectorService
				.getApplicationList(s2sOpportunity.getOpportunityId(),
						s2sOpportunity.getCfdaNumber(), s2sOpportunity
								.getProposalNumber());
		return applicationListResponse;
	}

	/**
	 * 
	 * This method fetches the status from Grants Gov and saves in case status
	 * is modified
	 * 
	 * @param pdDoc
	 *            {@link ProposalDevelopmentDocument}
	 * @param appSubmission
	 *            {@link S2sAppSubmission}
	 * @param applicationListResponse
	 *            {@link GetApplicationListResponse} response from Grants Gov
	 * @throws S2SException
	 */
	private void saveGrantsGovStatus(ProposalDevelopmentDocument pdDoc,
			GetApplicationListResponse applicationListResponse)
			throws S2SException {
		S2sAppSubmission appSubmission = null;
		List<S2sAppSubmission> appSubmissionList = pdDoc
				.getDevelopmentProposal().getS2sAppSubmission();
		int submissionNo = 0;
		for (S2sAppSubmission s2AppSubmission : appSubmissionList) {
			if (s2AppSubmission.getSubmissionNumber() > submissionNo) {
				appSubmission = s2AppSubmission;
				submissionNo = s2AppSubmission.getSubmissionNumber();
			}
		}

		if (appSubmission != null) {
			boolean statusChanged = false;
			if (applicationListResponse.getApplicationInformation() == null
					|| applicationListResponse.getApplicationInformation()
							.size() == 0) {
				statusChanged = checkForSubmissionStatusChange(pdDoc,
						appSubmission);
			} else {
				int appSize = applicationListResponse
						.getApplicationInformation().size();
				ApplicationInformationType ggApplication = applicationListResponse
						.getApplicationInformation().get(appSize - 1);
				if (ggApplication != null) {
					statusChanged = !appSubmission.getStatus()
							.equalsIgnoreCase(
									ggApplication
											.getGrantsGovApplicationStatus()
											.value());
					populateAppSubmission(appSubmission, ggApplication);
                    if (StringUtils.isNotBlank(appSubmission.getAgencyTrackingId())) {
                        pdDoc.getDevelopmentProposal().setSponsorProposalNumber(appSubmission.getAgencyTrackingId());
                    }					
				}
			}
			if (statusChanged) {
				businessObjectService.save(appSubmission);
			}
		}

	}

	/**
	 * This method checks if status on grants.gov has changed since last check
	 * and returns the status.
	 * 
	 * @param pdDoc
	 * @param appSubmission
	 * @return status
	 * @throws S2SException
	 */
	public boolean checkForSubmissionStatusChange(
			ProposalDevelopmentDocument pdDoc, S2sAppSubmission appSubmission)
			throws S2SException {
		boolean statusChanged = false;
		GetApplicationStatusDetailResponse applicationStatusDetailResponse = grantsGovConnectorService
				.getApplicationStatusDetail(appSubmission.getGgTrackingId(),
						pdDoc.getDevelopmentProposal().getProposalNumber());
		if (applicationStatusDetailResponse != null
				&& applicationStatusDetailResponse.getDetailedStatus() != null) {
			String statusDetail = applicationStatusDetailResponse
					.getDetailedStatus().toString();
			String statusStr = statusDetail.toUpperCase().indexOf(
					GRANTS_GOV_STATUS_ERROR) == -1 ? statusDetail
					: S2SConstants.STATUS_GRANTS_GOV_SUBMISSION_ERROR;
			statusChanged = !appSubmission.getStatus().equalsIgnoreCase(
					statusStr);
			appSubmission.setComments(statusDetail);
			appSubmission.setStatus(statusStr);
		} else {
			appSubmission
					.setComments(S2SConstants.STATUS_NO_RESPONSE_FROM_GRANTS_GOV);
		}
		return statusChanged;
	}

	/**
	 * This method populates the {@link S2sAppSubmission} BO with details from
	 * {@link ApplicationInformationType}
	 * 
	 * @param appSubmission
	 * @param ggApplication
	 */
	public void populateAppSubmission(S2sAppSubmission appSubmission,
			ApplicationInformationType ggApplication) {
		appSubmission.setGgTrackingId(ggApplication
				.getGrantsGovTrackingNumber());
		appSubmission
				.setReceivedDate(new Timestamp(ggApplication
						.getReceivedDateTime().toGregorianCalendar()
						.getTimeInMillis()));
		appSubmission.setStatus(ggApplication.getGrantsGovApplicationStatus()
				.toString());
		appSubmission.setLastModifiedDate(new Timestamp(ggApplication
				.getStatusDateTime().toGregorianCalendar().getTimeInMillis()));
		appSubmission.setAgencyTrackingId(ggApplication
				.getAgencyTrackingNumber());
		if (ggApplication.getAgencyTrackingNumber() != null
				&& ggApplication.getAgencyTrackingNumber().length() > 0) {
			appSubmission
					.setComments(S2SConstants.STATUS_AGENCY_TRACKING_NUMBER_ASSIGNED);
		} else {
			appSubmission.setComments(ggApplication
					.getGrantsGovApplicationStatus().toString());
		}
	}

	/**
	 * This method is to find the list of available opportunities for a given
	 * cfda number, opportunity ID and competition ID.
	 * 
	 * @param cfdaNumber
	 *            of the opportunity.
	 * @param opportunityId
	 *            parameter for the opportunity.
	 * @param competitionId
	 *            parameter for the opportunity.
	 * @return List<S2sOpportunity> a list containing the available
	 *         opportunities for the corresponding parameters.
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#searchOpportunity(java.lang.String,
	 *      java.lang.String, java.lang.String)
	 */
	public List<S2sOpportunity> searchOpportunity(String cfdaNumber,
			String opportunityId, String competitionId) throws S2SException {
		List<S2sOpportunity> s2sOpportunityList;
		s2sOpportunityList = convertToArrayList(grantsGovConnectorService
				.getOpportunityList(cfdaNumber, opportunityId, competitionId));
		return s2sOpportunityList;
	}

	/**
	 * This method is used to submit forms to Grants.gov. It generates forms for
	 * a given {@link ProposalDevelopmentDocument}, validates and then submits
	 * the forms
	 * 
	 * @param pdDoc
	 *            Proposal Development Document.
	 * @return true if submitted false otherwise.
	 * @throws S2SException
	 * @see org.kuali.kra.s2s.service.S2SService#submitApplication(org.kuali.kra.proposaldevelopment.document.ProposalDevelopmentDocument)
	 */
	public boolean submitApplication(ProposalDevelopmentDocument pdDoc)
			throws S2SException {
		boolean submissionStatus = false;
		Forms forms = Forms.Factory.newInstance();
		List<AttachmentData> attList = new ArrayList<AttachmentData>();
		if (generateAndValidateForms(forms, attList, pdDoc)) {
			GrantApplicationDocument grantApplicationDocument = getGrantApplicationDocument(
					pdDoc, forms);

			Map<String, DataHandler> attachments = new HashMap<String, DataHandler>();
			List<S2sAppAttachments> s2sAppAttachmentList = new ArrayList<S2sAppAttachments>();
			DataHandler attachmentFile;
			for (AttachmentData attachmentData : attList) {
				attachmentFile = new DataHandler(new ByteArrayDataSource(
						attachmentData.getContent(), attachmentData
								.getContentType()));

				attachments.put(attachmentData.getContentId(), attachmentFile);
				S2sAppAttachments appAttachments = new S2sAppAttachments();
				appAttachments.setContentId(attachmentData.getContentId());
				appAttachments.setProposalNumber(pdDoc.getDevelopmentProposal()
						.getProposalNumber());
				s2sAppAttachmentList.add(appAttachments);
			}
			S2sAppSubmission appSubmission = new S2sAppSubmission();
			appSubmission.setStatus(S2SConstants.GRANTS_GOV_STATUS_MESSAGE);
			appSubmission.setComments(S2SConstants.GRANTS_GOV_COMMENTS_MESSAGE);
			SubmitApplicationResponse response = null;
			XmlOptions xmlOptions = new XmlOptions();
			Map<String, String> prefixMap = new HashMap<String, String>();
			prefixMap.put(
					"http://apply.grants.gov/system/MetaGrantApplication", "");
			prefixMap.put(
					"http://apply.grants.gov/forms/RR_SubawardBudget-V1.0",
					"RR_SubawardBudget");
			prefixMap.put(
					"http://apply.grants.gov/forms/RR_SubawardBudget-V1.1",
					"RR_SubawardBudget");
			prefixMap.put(
					"http://apply.grants.gov/forms/RR_SubawardBudget-V1.2",
					"RR_SubawardBudget");

			xmlOptions.setSaveSuggestedPrefixes(prefixMap);

			String applicationXml = grantApplicationDocument
					.xmlText(xmlOptions);
			try {
				response = grantsGovConnectorService.submitApplication(
						applicationXml, attachments, pdDoc
								.getDevelopmentProposal().getProposalNumber());
	            appSubmission
	            .setStatus(S2SConstants.GRANTS_GOV_SUBMISSION_MESSAGE);
			} catch (S2SException ex) {
				appSubmission
						.setStatus(S2SConstants.STATUS_GRANTS_GOV_SUBMISSION_ERROR);
				StringBuilder errMsg = new StringBuilder();
				errMsg.append(S2SConstants.GRANTS_GOV_SUBMISION_ERROR_MESSAGE);
				errMsg.append(ex.getMessage());
				appSubmission.setComments(errMsg.toString());
				businessObjectService.save(appSubmission);
				throw ex;
			}
			saveSubmissionDetails(pdDoc, appSubmission, response,
					applicationXml, s2sAppAttachmentList);
			submissionStatus = true;
{'fraction_non_alphanumeric': 0.08656762612194367, 'fraction_numerical': 0.009377901578458681, 'mean_word_length': 4.877933418228125, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 38, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '679185', 'n_tokens_mistral': 10965, 'n_tokens_neox': 9564, 'n_words': 1802}