View Javadoc
1   /*
2    * Kuali Coeus, a comprehensive research administration system for higher education.
3    * 
4    * Copyright 2005-2015 Kuali, Inc.
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.coeus.s2sgen.impl.generate;
20  
21  
22  import gov.grants.apply.system.attachmentsV10.AttachedFileDataType;
23  import gov.grants.apply.system.attachmentsV10.AttachedFileDataType.FileLocation;
24  import gov.grants.apply.system.globalV10.HashValueDocument.HashValue;
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.xmlbeans.impl.util.Base64;
27  import org.kuali.coeus.common.questionnaire.api.answer.AnswerContract;
28  import org.kuali.coeus.common.questionnaire.api.answer.AnswerHeaderContract;
29  import org.kuali.coeus.common.questionnaire.api.core.QuestionAnswerService;
30  import org.kuali.coeus.propdev.api.core.DevelopmentProposalContract;
31  import org.kuali.coeus.propdev.api.person.ProposalPersonContract;
32  import org.kuali.coeus.propdev.api.questionnaire.PropDevQuestionAnswerService;
33  import org.kuali.coeus.propdev.api.person.attachment.ProposalPersonBiographyContract;
34  import org.kuali.coeus.propdev.api.core.ProposalDevelopmentDocumentContract;
35  import org.kuali.coeus.common.api.sponsor.hierarchy.SponsorHierarchyService;
36  import org.kuali.coeus.propdev.api.attachment.NarrativeContract;
37  import org.kuali.coeus.propdev.api.attachment.NarrativeService;
38  import org.kuali.coeus.s2sgen.api.core.InfastructureConstants;
39  import org.kuali.coeus.s2sgen.api.generate.AttachmentData;
40  import org.kuali.coeus.s2sgen.api.generate.FormMappingInfo;
41  import org.kuali.coeus.s2sgen.api.generate.FormMappingService;
42  import org.kuali.coeus.s2sgen.impl.generate.support.GlobalLibraryV1_0Generator;
43  import org.kuali.coeus.s2sgen.impl.generate.support.GlobalLibraryV2_0Generator;
44  import org.kuali.coeus.s2sgen.api.core.AuditError;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  import org.springframework.beans.factory.BeanNameAware;
48  import org.springframework.beans.factory.InitializingBean;
49  import org.springframework.beans.factory.annotation.Autowired;
50  import org.springframework.beans.factory.annotation.Qualifier;
51  import org.springframework.core.io.Resource;
52  import org.w3c.dom.Document;
53  import org.w3c.dom.NodeList;
54  
55  import javax.xml.parsers.DocumentBuilder;
56  import javax.xml.parsers.DocumentBuilderFactory;
57  import java.io.ByteArrayInputStream;
58  import java.security.MessageDigest;
59  import java.security.NoSuchAlgorithmException;
60  import java.util.ArrayList;
61  import java.util.List;
62  import java.util.regex.Matcher;
63  import java.util.regex.Pattern;
64  
65  /**
66   * 
67   * This class defines the Base methods for the Form Generator classes
68   * 
69   * @author Kuali Research Administration Team (kualidev@oncourse.iu.edu)
70   */
71  public abstract class S2SBaseFormGenerator implements S2SFormGenerator, InitializingBean, BeanNameAware {
72  
73      private static final Logger LOG = LoggerFactory.getLogger(S2SBaseFormGenerator.class);
74  
75      protected static final String NOT_ANSWERED = "No";
76      protected static final String KEY_VALUE_SEPARATOR = "-";
77      protected static final String AREAS_AFFECTED_ABSTRACT_TYPE_CODE="16";
78      protected static final int ORGANIZATON_NAME_MAX_LENGTH = 60;
79      protected static final int DUNS_NUMBER_MAX_LENGTH = 13;
80      protected static final int PRIMARY_TITLE_MAX_LENGTH = 45;
81      protected static final int CONGRESSIONAL_DISTRICT_MAX_LENGTH = 6;
82      private static final String NARRATIVE_ATTACHMENT_FILE_LOCATION = "att:FileLocation";
83      protected static final String DEFAULT_SORT_INDEX = "1000";
84      private static final String REPLACEMENT_CHARACTER = "_";
85      private static final String REGEX_TITLE_FILENAME_PATTERN = "([^0-9a-zA-Z\\.\\-_])";
86  
87      protected ProposalDevelopmentDocumentContract pdDoc = null;
88      private List<AuditError> auditErrors = new ArrayList<>();
89      private List<AttachmentData> attachments = new ArrayList<>();
90  
91      private String beanName;
92  
93      @Autowired
94      @Qualifier("narrativeService")
95      private NarrativeService narrativeService;
96  
97      @Autowired
98      @Qualifier("propDevQuestionAnswerService")
99      private PropDevQuestionAnswerService propDevQuestionAnswerService;
100 
101     @Autowired
102     @Qualifier("questionAnswerService")
103     private QuestionAnswerService questionAnswerService;
104 
105     @Autowired
106     @Qualifier("sponsorHierarchyService")
107     private SponsorHierarchyService sponsorHierarchyService;
108 
109     @Autowired
110     @Qualifier("formMappingService")
111     private FormMappingService formMappingService;
112 
113     /*
114      * Reference to global library generators are defined here. The actual form generator will decide which object to be used for
115      * respective implementations.
116      */
117     @Autowired
118     @Qualifier("GlobalLibraryV1_0Generator")
119     protected GlobalLibraryV1_0Generator globLibV10Generator;
120 
121     @Autowired
122     @Qualifier("GlobalLibraryV2_0Generator")
123     protected GlobalLibraryV2_0Generator globLibV20Generator;
124 
125     /**
126      * Gets the list of attachments associated with a form. As the form generator fills the form data, the attachment information is
127      * stored into the instance variable
128      * 
129      * @return List<AttachementData> List of attachments associated with the the form. Returns an empty list if no attachment is
130      *         available.
131      */
132 
133     public List<AttachmentData> getAttachments() {
134         return attachments;
135     }
136 
137     /**
138      * 
139      * This is for adding attachment for the forms during form generation.
140      * 
141      * @param attachment - The attachment data to add.
142      */
143     protected void addAttachment(AttachmentData attachment) {
144         attachments.add(attachment);
145     }
146 
147     /**
148      * 
149      * This method is used to generate the HashValue for a particular file stream. The hashing algorithm is defined in constant
150      * S2SConstants.HASH_ALGORITHM
151      * 
152      * @param fileData - They byte[] containing the file data.
153      * @return HashValue - The hash value calculated for the fileData input.
154      */
155     protected HashValue getHashValue(byte[] fileData) {
156         return createHashValue(computeAttachmentHash(fileData));
157     }
158     /**
159      * 
160      * Generates the contentId or href for narrative attachments in S2S
161      */
162     public String createContentId(NarrativeContract narrative) {
163         String retVal = "N-" + narrative.getModuleNumber();
164         if(narrative.getNarrativeType()!=null){
165             if (narrative.getNarrativeType().isAllowMultiple() &&
166                     StringUtils.isNotBlank(narrative.getModuleTitle())) {
167                 retVal += "_" + narrative.getModuleTitle().trim();
168             }else{
169                 retVal += "_" + narrative.getNarrativeType().getDescription().trim();
170             }
171         }
172         int index = getIndexOfAttachmentAlreadyAdded(retVal);
173         if(index > 0){
174             retVal+=("-"+index);
175         }
176         return retVal;
177     }
178     
179     private int getIndexOfAttachmentAlreadyAdded(String contentId) {
180         int index = 0;
181         List<AttachmentData> attachments = getAttachments();
182         for (AttachmentData attachmentData : attachments) {
183             String attContentId = attachmentData.getContentId();
184             int lastIndex = attContentId.indexOf('-',6);
185             if(lastIndex!=-1){
186                 attContentId = attContentId.substring(0, attContentId.lastIndexOf('-'));
187             }
188             if(attContentId.equals(contentId)){
189                 index++;
190             }
191         }
192         return index;
193     }
194 
195     public String createContentId(ProposalPersonBiographyContract biography) {
196         String retVal = "B-" + biography.getProposalPersonNumber() + "_" + biography.getBiographyNumber();
197         if(biography.getPropPerDocType() != null) 
198             retVal += "_" + biography.getPropPerDocType().getDescription().trim();
199         if (StringUtils.isNotBlank(biography.getDescription())) {
200             retVal += "_" + biography.getDescription().trim();
201         }
202         int index = getIndexOfAttachmentAlreadyAdded(retVal);
203         if(index > 0){
204             retVal+=index;
205         }
206         return retVal;
207     }
208 
209 
210 
211     /**
212      * 
213      * This method creates and returns Hash Value for particular form
214      * 
215      * @param hashValueStr
216      * @return hashValue (HashValue)
217      * 
218      */
219     private synchronized static HashValue createHashValue(byte[] hashValueStr) {
220         HashValue hashValue = null;
221         hashValue = HashValue.Factory.newInstance();
222         hashValue.setHashAlgorithm(InfastructureConstants.HASH_ALGORITHM);
223         hashValue.setByteArrayValue(Base64.decode(hashValueStr));
224         return hashValue;
225     }
226 
227     /**
228      * 
229      * This method is used to encode the hash value based on Message Digest
230      * 
231      * @param attachment
232      * @return Base64.encode(rawDigest) (String)
233      */
234     protected final static byte[] computeAttachmentHash(byte[] attachment) {
235         MessageDigest messageDigester;
236         try {
237             messageDigester = MessageDigest.getInstance(InfastructureConstants.HASH_ALGORITHM);
238             byte[] rawDigest = messageDigester.digest(attachment);
239             return Base64.encode(rawDigest);
240         }
241         catch (NoSuchAlgorithmException e) {
242             throw new RuntimeException(e);
243         }
244     }
245 
246     protected AttachedFileDataType getAttachedFileType(NarrativeContract narrative) {
247         AttachedFileDataType attachedFileDataType = null;
248         byte[] attachementContent = null;
249         if(narrative.getNarrativeAttachment()!= null){
250         	attachementContent = narrative.getNarrativeAttachment().getData();
251         }
252 	    if(attachementContent != null && attachementContent.length > 0 ){
253 	    	
254 	        FileLocation fileLocation = FileLocation.Factory.newInstance();
255 	        String contentId = createContentId(narrative);
256 	        fileLocation.setHref(contentId);
257 	    	
258 	        attachedFileDataType = AttachedFileDataType.Factory.newInstance();
259 	        attachedFileDataType.setFileLocation(fileLocation);
260 	        attachedFileDataType.setFileName(getS2sNarrativeFileName(narrative));
261 	        attachedFileDataType.setMimeType(InfastructureConstants.CONTENT_TYPE_OCTET_STREAM);
262 			attachedFileDataType.setHashValue(getHashValue(attachementContent));
263 	        AttachmentData attachmentData = new AttachmentData();
264 	        attachmentData.setContent(attachementContent);
265 	        attachmentData.setContentId(contentId);
266 	        attachmentData.setContentType(InfastructureConstants.CONTENT_TYPE_OCTET_STREAM);
267 	        attachmentData.setFileName(getS2sNarrativeFileName(narrative));
268 	        addAttachment(attachmentData);
269 	    }
270         return attachedFileDataType;
271     }
272     /**
273      * 
274      * This method is used to get List of Other attachments from NarrativeAttachment
275      * 
276      * @return AttachedFileDataType[] based on the narrative type code.
277      */
278     protected AttachedFileDataType[] getAttachedFileDataTypes(String narrativeTypeCode) {
279 
280         int size = 0;
281         for (NarrativeContract narrative : pdDoc.getDevelopmentProposal().getNarratives()) {
282             if (narrative.getNarrativeType() != null
283                     && narrative.getNarrativeType().getCode().equals(narrativeTypeCode)) {
284                 size++;
285             }
286         }
287         AttachedFileDataType[] attachedFileDataTypes = new AttachedFileDataType[size];
288         int attachments = 0;
289         for (NarrativeContract narrative : pdDoc.getDevelopmentProposal().getNarratives()) {
290             if (narrative.getNarrativeType() != null
291                     && narrative.getNarrativeType().getCode().equals(narrativeTypeCode)) {
292                 attachedFileDataTypes[attachments] = getAttachedFileType(narrative);
293                 attachments++;
294             }
295         }
296         return attachedFileDataTypes;
297     }
298     /**
299      * 
300      * This method is used to get List of Other attachments from NarrativeAttachment
301      * 
302      * @return AttachedFileDataType[] based on the narrative type code.
303      */
304     protected AttachedFileDataType getAttachedFileDataType(String narrativeTypeCode) {
305 
306         for (NarrativeContract narrative : pdDoc.getDevelopmentProposal().getNarratives()) {
307             if (narrative.getNarrativeType() != null
308                     && narrative.getNarrativeType().getCode().equals(narrativeTypeCode)) {
309                 return getAttachedFileType(narrative);
310             }
311         }
312         return null;
313     }
314 
315     /**
316      * 
317      * This method fetches the attachments for {@link org.kuali.coeus.propdev.api.person.ProposalPersonContract}. For a given person or rolodex ID, it will fetch the document
318      * of required type, also passed alongside as documentType
319      * 
320      * @param pdDoc {@link ProposalDevelopmentDocumentContract} from which the attachments are to be fetched
321      * @param personId ID of the proposal person
322      * @param rolodexId Rolodex ID of the person
323      * @param documentType type of document thats to be fetched
324      * @return {@link AttachedFileDataType} containing the required document
325      */
326     protected AttachedFileDataType getPernonnelAttachments(ProposalDevelopmentDocumentContract pdDoc, String personId, Integer rolodexId,
327             String documentType) {
328         boolean personBiographyFound = false;
329         for (ProposalPersonBiographyContract proposalPersonBiography : pdDoc.getDevelopmentProposal().getPropPersonBios()) {
330             if (personId != null && proposalPersonBiography.getPersonId() != null
331                     && proposalPersonBiography.getPersonId().equals(personId)
332                     && documentType.equals(proposalPersonBiography.getPropPerDocType().getCode())) {
333                 personBiographyFound = true;
334             }
335             else if (rolodexId != null && proposalPersonBiography.getRolodexId() != null
336                     && proposalPersonBiography.getRolodexId().toString().equals(rolodexId.toString())
337                     && proposalPersonBiography.getPropPerDocType() != null
338                     && documentType.equals(proposalPersonBiography.getPropPerDocType().getCode())) {
339                 personBiographyFound = true;
340             }
341             byte[] attachmentContent = null;
342             if(proposalPersonBiography.getPersonnelAttachment() != null){
343                 attachmentContent = proposalPersonBiography.getPersonnelAttachment().getData();
344             }
345 
346             if (personBiographyFound && attachmentContent != null && attachmentContent.length > 0) {
347                 FileLocation fileLocation = FileLocation.Factory.newInstance();
348                 String contentId = createContentId(proposalPersonBiography);
349                 fileLocation.setHref(contentId);
350                 AttachedFileDataType attachedFileDataType = AttachedFileDataType.Factory.newInstance();
351                 attachedFileDataType.setFileLocation(fileLocation);
352                 attachedFileDataType.setFileName(getS2sPersonnelAttachmentFileName(pdDoc.getDevelopmentProposal(),proposalPersonBiography));
353                 attachedFileDataType.setMimeType(InfastructureConstants.CONTENT_TYPE_OCTET_STREAM);
354                 attachedFileDataType.setHashValue(getHashValue(proposalPersonBiography.getPersonnelAttachment()
355                         .getData()));
356                 AttachmentData attachmentData = new AttachmentData();
357                 attachmentData.setContent(proposalPersonBiography.getPersonnelAttachment().getData());
358                 attachmentData.setContentId(contentId);
359                 attachmentData.setContentType(InfastructureConstants.CONTENT_TYPE_OCTET_STREAM);
360                 attachmentData.setFileName(getS2sPersonnelAttachmentFileName(pdDoc.getDevelopmentProposal(),proposalPersonBiography));
361                 addAttachment(attachmentData);
362                 return attachedFileDataType;
363             }
364         }
365         return null;
366     }
367 
368     protected String getS2sNarrativeFileName(NarrativeContract narrative){
369         String fileName = null;
370         if (narrative.getNarrativeType().isAllowMultiple()) {
371             fileName = narrative.getModuleTitle();
372         } else {
373             fileName = narrative.getNarrativeType().getDescription();
374         }
375         String extension = StringUtils.substringAfter(narrative.getNarrativeAttachment().getName(),".");
376         return cleanFileName(fileName) + "." + extension;
377     }
378 
379     protected String getS2sPersonnelAttachmentFileName(DevelopmentProposalContract developmentProposal, ProposalPersonBiographyContract biography) {
380 
381         String extension = StringUtils.substringAfter(biography.getName(),".");
382         String fullName = getPerson(developmentProposal,biography.getProposalPersonNumber()).getFullName();
383         String docType = biography.getPropPerDocType().getDescription();
384         String fileName = fullName + "_" + docType;
385 
386         return cleanFileName(fileName) + "." + extension;
387 
388     }
389 
390     protected String  cleanFileName(String fileName) {
391         Pattern pattern = Pattern.compile(REGEX_TITLE_FILENAME_PATTERN);
392         Matcher matcher = pattern.matcher(fileName);
393         return matcher.replaceAll(REPLACEMENT_CHARACTER);
394     }
395 
396     protected ProposalPersonContract getPerson(DevelopmentProposalContract developmentProposal, Integer proposalPersonNumber) {
397         for (ProposalPersonContract person : developmentProposal.getProposalPersons()) {
398             if (proposalPersonNumber.equals(person.getProposalPersonNumber())) {
399                 return person;
400             }
401         }
402         return null;
403     }
404 
405     /**
406      * Gets the auditErrors attribute. 
407      * @return Returns the auditErrors.
408      */
409     public List<AuditError> getAuditErrors() {
410         return auditErrors;
411     }
412 
413     /**
414      * Sets the auditErrors attribute value.
415      * @param auditErrors The auditErrors to set.
416      */
417     public void setAuditErrors(List<AuditError> auditErrors) {
418         this.auditErrors = auditErrors;
419     }
420     
421     protected boolean isSponsorNIH(ProposalDevelopmentDocumentContract document) {
422 		return sponsorHierarchyService.isSponsorNihMultiplePi(document.getDevelopmentProposal().getSponsor().getSponsorCode());
423 	}
424     
425 	protected NarrativeContract saveNarrative(byte[] attachment, String narrativeTypeCode,String fileName,String comment) {
426         return narrativeService.createSystemGeneratedNarrative(pdDoc.getDevelopmentProposal().getProposalNumber(), narrativeTypeCode, attachment, fileName, comment);
427 	}
428 
429     /**
430      * Sets the attachments attribute value.
431      * @param attachments The attachments to set.
432      */
433     public void setAttachments(List<AttachmentData> attachments) {
434         this.attachments = attachments;
435     }
436 
437     /**
438      * Sort the attachments.
439      * @param byteArrayInputStream
440      */
441     public void sortAttachments(ByteArrayInputStream byteArrayInputStream)  {
442         List<String> attachmentNameList = new ArrayList<String> ();
443         List<AttachmentData> attacmentList = getAttachments();
444         List<AttachmentData> tempAttacmentList = new ArrayList<AttachmentData>();
445         
446         try{
447             DocumentBuilderFactory domParserFactory = DocumentBuilderFactory.newInstance();
448             DocumentBuilder domParser = domParserFactory.newDocumentBuilder();
449             Document document = domParser.parse(byteArrayInputStream);
450             byteArrayInputStream.close();
451             NodeList fileLocationList = document.getElementsByTagName(NARRATIVE_ATTACHMENT_FILE_LOCATION);                       
452             
453            for(int itemLocation=0;itemLocation<fileLocationList.getLength();itemLocation++){
454                String attachmentName =fileLocationList.item(itemLocation).getAttributes().item(0).getNodeValue();
455                String[] name = attachmentName.split(KEY_VALUE_SEPARATOR);               
456                String fileName =name[name.length-1];
457                attachmentNameList.add(fileName);
458            }
459         }catch (Exception e) {
460             LOG.error(e.getMessage(), e);
461         }
462         
463         for(String attachmentName :attachmentNameList){
464             for(AttachmentData attachment : attacmentList){                
465                 String[] names = attachment.getContentId().split(KEY_VALUE_SEPARATOR);               
466                 String fileName =names[names.length-1];
467                 if(fileName.equalsIgnoreCase(attachmentName)){
468                     tempAttacmentList.add(attachment);
469                 }
470             }
471         }
472         if(tempAttacmentList.size() > 0){
473             attachments.clear();
474             for(AttachmentData tempAttachment :tempAttacmentList){
475                 attachments.add(tempAttachment);
476             } 
477         }
478     }
479 
480     /**
481      *
482      * This method is used to get the answer for a particular Questionnaire question
483      * question based on the question id.
484      *
485      * @param questionSeqId
486      *            the question seq id to be passed.
487      * @return returns the answer for a particular
488      *         question based on the question id passed.
489      */
490     protected String getAnswer(Integer questionSeqId, List<? extends AnswerHeaderContract> answerHeaders) {
491         for(AnswerHeaderContract answerHeader:answerHeaders){
492             if(answerHeader!=null){
493                 List<? extends AnswerContract> answerDetails = answerHeader.getAnswers();
494                 for(AnswerContract answers:answerDetails){
495                     if(questionSeqId.equals(getQuestionAnswerService().findQuestionById(answers.getQuestionId()).getQuestionSeqId())){
496                         return answers.getAnswer();
497                     }
498                 }
499             }
500         }
501 
502         return null;
503     }
504 
505     /**
506      *
507      * This method is used to get the answerId for a particular Questionnaire question
508      * question based on the question id.
509      *
510      * @param questionSeqId
511      *            the question seq id to be passed.
512      * @return returns the answer for a particular
513      *         question based on the question id passed.
514      */
515     protected Long getAnswerId(Integer questionSeqId, List<? extends AnswerHeaderContract> answerHeaders) {
516         if (answerHeaders != null && !answerHeaders.isEmpty()) {
517             for (AnswerHeaderContract answerHeader : answerHeaders) {
518                 List<? extends AnswerContract> answerDetails = answerHeader.getAnswers();
519                 for (AnswerContract answers : answerDetails) {
520                     if (answers.getAnswer() != null && questionSeqId.equals(getQuestionAnswerService().findQuestionById(answers.getQuestionId()).getQuestionSeqId())) {
521                         return answers.getId();
522                     }
523                 }
524             }
525         }
526         return null;
527     }
528     /**
529      *
530      * This method is used to get the child question answer for a particular Questionnaire question
531      * question based on the question id.
532      * @param parentQuestionSeqId
533      *            the parentQuestion id to be passed.
534      * @param questionSeqId
535      *            the question id to be passed.
536      * @return returns the answer for a particular
537      *         question based on the question id passed.
538      */
539     protected String getChildQuestionAnswer(Integer parentQuestionSeqId,Integer questionSeqId, List<? extends AnswerHeaderContract> answerHeaders) {
540         for(AnswerHeaderContract answerHeader:answerHeaders){
541             if(answerHeader!=null){
542                 List<? extends AnswerContract> answerDetails = answerHeader.getAnswers();
543                 for(AnswerContract answers:answerDetails){
544                     if(answers.getParentAnswers()!= null){
545                         AnswerContract parentAnswer =  answers.getParentAnswers().get(0);
546                         if(questionSeqId.equals(getQuestionAnswerService().findQuestionById(answers.getQuestionId()).getQuestionSeqId()) &&
547                                 parentQuestionSeqId.equals(getQuestionAnswerService().findQuestionById(parentAnswer.getQuestionId()).getQuestionSeqId()) ){
548                             return answers.getAnswer();
549                         }
550                     }
551                 }
552             }
553         }
554 
555         return null;
556 
557     }
558 
559     /*
560   * This method will get the childAnswer for sub question
561   */
562     protected String getAnswers(Integer questionSeqId, List<? extends AnswerHeaderContract> answerHeaders) {
563 
564         String answer = null;
565         String childAnswer = null;
566         StringBuilder stringBuilder = new StringBuilder();
567         if (answerHeaders != null && !answerHeaders.isEmpty()) {
568             for (AnswerHeaderContract answerHeader : answerHeaders) {
569                 List<? extends AnswerContract> answerDetails = answerHeader.getAnswers();
570                 for (AnswerContract answers : answerDetails) {
571                     if (questionSeqId.equals(getQuestionAnswerService().findQuestionById(answers.getQuestionId()).getQuestionSeqId())) {
572                         answer = answers.getAnswer();
573                         if (answer != null) {
574                             if (!answer.equals(NOT_ANSWERED)) {
575                                 stringBuilder.append(answer);
576                                 stringBuilder.append(",");
577                             }
578                         }
579                         childAnswer = stringBuilder.toString();
580                     }
581                 }
582             }
583         }
584         return childAnswer;
585     }
586 
587     @Override
588     public void setBeanName(String beanName) {
589         this.beanName = beanName;
590     }
591 
592     public abstract String getNamespace();
593 
594     public String getGeneratorName() {
595         return beanName;
596     }
597 
598     public abstract String getFormName();
599 
600     public abstract Resource getStylesheet();
601 
602     public abstract String getPackageName();
603 
604     public abstract int getSortIndex();
605 
606     public NarrativeService getNarrativeService() {
607         return narrativeService;
608     }
609 
610     public PropDevQuestionAnswerService getPropDevQuestionAnswerService() {
611         return propDevQuestionAnswerService;
612     }
613 
614     public QuestionAnswerService getQuestionAnswerService() {
615         return questionAnswerService;
616     }
617 
618     public void setNarrativeService(NarrativeService narrativeService) {
619         this.narrativeService = narrativeService;
620     }
621 
622     public void setPropDevQuestionAnswerService(PropDevQuestionAnswerService propDevQuestionAnswerService) {
623         this.propDevQuestionAnswerService = propDevQuestionAnswerService;
624     }
625 
626     public void setQuestionAnswerService(QuestionAnswerService questionAnswerService) {
627         this.questionAnswerService = questionAnswerService;
628     }
629 
630     public SponsorHierarchyService getSponsorHierarchyService() {
631         return sponsorHierarchyService;
632     }
633 
634     public void setSponsorHierarchyService(SponsorHierarchyService sponsorHierarchyService) {
635         this.sponsorHierarchyService = sponsorHierarchyService;
636     }
637 
638     public FormMappingService getFormMappingService() {
639         return formMappingService;
640     }
641 
642     public void setFormMappingService(FormMappingService formMappingService) {
643         this.formMappingService = formMappingService;
644     }
645 
646     public GlobalLibraryV1_0Generator getGlobLibV10Generator() {
647         return globLibV10Generator;
648     }
649 
650     public void setGlobLibV10Generator(GlobalLibraryV1_0Generator globLibV10Generator) {
651         this.globLibV10Generator = globLibV10Generator;
652     }
653 
654     public GlobalLibraryV2_0Generator getGlobLibV20Generator() {
655         return globLibV20Generator;
656     }
657 
658     public void setGlobLibV20Generator(GlobalLibraryV2_0Generator globLibV20Generator) {
659         this.globLibV20Generator = globLibV20Generator;
660     }
661 
662     @Override
663     public void afterPropertiesSet() throws Exception {
664         formMappingService.registerForm(new FormMappingInfo(getNamespace(), getGeneratorName(), getFormName(), getStylesheet().getURL().toString(), getSortIndex(), false));
665     }
666 }