View Javadoc

1   /*
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.lum.program.service.assembler;
17  
18  import static org.apache.commons.lang.StringUtils.isEmpty;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.kuali.student.common.assembly.BaseDTOAssemblyNode;
27  import org.kuali.student.common.assembly.BaseDTOAssemblyNode.NodeOperation;
28  import org.kuali.student.common.assembly.data.AssemblyException;
29  import org.kuali.student.common.dto.DtoConstants;
30  import org.kuali.student.common.dto.RichTextInfo;
31  import org.kuali.student.common.exceptions.DoesNotExistException;
32  import org.kuali.student.common.exceptions.InvalidParameterException;
33  import org.kuali.student.common.exceptions.MissingParameterException;
34  import org.kuali.student.common.exceptions.OperationFailedException;
35  import org.kuali.student.common.util.UUIDHelper;
36  import org.kuali.student.lum.lu.dto.AdminOrgInfo;
37  import org.kuali.student.lum.lu.dto.CluCluRelationInfo;
38  import org.kuali.student.lum.lu.dto.CluIdentifierInfo;
39  import org.kuali.student.lum.lu.dto.CluInfo;
40  import org.kuali.student.lum.lu.dto.CluPublicationInfo;
41  import org.kuali.student.lum.lu.dto.CluResultInfo;
42  import org.kuali.student.lum.lu.dto.FieldInfo;
43  import org.kuali.student.lum.lu.dto.LuCodeInfo;
44  import org.kuali.student.lum.lu.service.LuService;
45  import org.kuali.student.lum.program.dto.CredentialProgramInfo;
46  import org.kuali.student.lum.program.dto.assembly.ProgramAtpAssembly;
47  import org.kuali.student.lum.program.dto.assembly.ProgramBasicOrgAssembly;
48  import org.kuali.student.lum.program.dto.assembly.ProgramCodeAssembly;
49  import org.kuali.student.lum.program.dto.assembly.ProgramCommonAssembly;
50  import org.kuali.student.lum.program.dto.assembly.ProgramCredentialAssembly;
51  import org.kuali.student.lum.program.dto.assembly.ProgramFullOrgAssembly;
52  import org.kuali.student.lum.program.dto.assembly.ProgramIdentifierAssembly;
53  import org.kuali.student.lum.program.dto.assembly.ProgramPublicationAssembly;
54  import org.kuali.student.lum.program.dto.assembly.ProgramRequirementAssembly;
55  import org.kuali.student.lum.service.assembler.CluAssemblerUtils;
56  
57  public class ProgramAssemblerUtils {
58  
59      private LuService luService;
60      private CluAssemblerUtils cluAssemblerUtils;
61  
62       /**
63       * Copy basic values from clu to program
64       *
65       * @param clu
66       * @param program
67       * @return
68       * @throws AssemblyException
69       */
70       public ProgramCommonAssembly assembleBasics(CluInfo clu, ProgramCommonAssembly program) throws AssemblyException {
71  
72           if (program instanceof CredentialProgramInfo) {
73               ((CredentialProgramInfo)program).setCredentialProgramType(clu.getType());
74           }
75           else {
76               program.setType(clu.getType());
77           }
78           program.setState(clu.getState());
79           program.setMetaInfo(clu.getMetaInfo());
80           program.setAttributes(clu.getAttributes());
81           program.setId(clu.getId());
82  
83           return program;
84       }
85  
86      /**
87       * Copy basic values from program to clu
88       *
89       * @param clu
90       * @param program
91       * @param operation
92       * @return
93       * @throws AssemblyException
94       */
95      public CluInfo disassembleBasics(CluInfo clu, ProgramCommonAssembly program) throws AssemblyException {
96  
97          if (program instanceof CredentialProgramInfo) {
98              clu.setType (((CredentialProgramInfo)program).getCredentialProgramType());
99          }
100         else {
101             clu.setType(program.getType());
102         }
103         clu.setId(UUIDHelper.genStringUUID(program.getId()));
104         
105         // Default 
106         clu.setState(program.getState());
107         clu.setMetaInfo(program.getMetaInfo());
108         clu.setAttributes(program.getAttributes());
109         return clu;
110 
111     }
112 
113     //TODO maybe this should be in CluAssemblerUtils??
114     public ProgramRequirementAssembly assembleRequirements(CluInfo clu, ProgramRequirementAssembly program) throws AssemblyException {
115 
116         try {
117             List<String> requirements = luService.getRelatedCluIdsByCluId(clu.getId(), ProgramAssemblerConstants.HAS_PROGRAM_REQUIREMENT);
118             if (requirements != null && requirements.size() > 0) {
119                 program.setProgramRequirements(requirements);
120             }
121         }
122         catch (Exception e)
123         {
124             throw new AssemblyException("Error assembling program requirements", e);
125         }
126 
127         return program;
128     }
129 
130     //TODO  maybe this should be in CluAssemblerUtils??
131     public CluInfo disassembleRequirements(CluInfo clu, ProgramRequirementAssembly program, NodeOperation operation, BaseDTOAssemblyNode<?, ?> result, boolean stateChanged) throws AssemblyException {
132         try {
133             List<String> requirements = program.getProgramRequirements ();
134 
135             if (requirements != null && !requirements.isEmpty()) {
136             	if (stateChanged){
137             		addUpdateRequirementStateNodes(requirements, program.getState(), result);
138             	}
139             	
140                	Map<String, String> currentRelations = null;
141 
142                 if (!NodeOperation.CREATE.equals(operation)) {
143                 	currentRelations = getCluCluRelations(clu.getId(), ProgramAssemblerConstants.HAS_PROGRAM_REQUIREMENT);
144                 }
145                 
146     	    	for (String requirementId : requirements){
147     	    		List<BaseDTOAssemblyNode<?, ?>> reqResults = addAllRelationNodes(clu.getId(), requirementId, ProgramAssemblerConstants.HAS_PROGRAM_REQUIREMENT, operation, currentRelations);
148     	            if (reqResults != null && reqResults.size()> 0) {
149     	                result.getChildNodes().addAll(reqResults);
150     	            }
151     	    	}
152     	    	
153     	        if(currentRelations != null && currentRelations.size() > 0){
154 	    	        for (Map.Entry<String, String> entry : currentRelations.entrySet()) {
155 	    	            // Create a new relation with the id of the relation we want to
156 	    	            // delete
157 	    	            CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
158 	    	            relationToDelete.setId( entry.getValue() );
159 	    	            BaseDTOAssemblyNode<Object, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
160 	    	                    null);
161 	    	            relationToDeleteNode.setNodeData(relationToDelete);
162 	    	            relationToDeleteNode.setOperation(NodeOperation.DELETE);
163 	    	            result.getChildNodes().add(relationToDeleteNode);
164 	    	        }
165     	        }
166             }
167 	    } catch (Exception e) {
168 	        throw new AssemblyException("Error while disassembling program requirements", e);
169 	    }
170 	    
171         return clu;
172 
173     }
174 
175         
176     /**
177      * This adds nodes to update the state for the requirement clu
178      * @param state
179      * @param result
180      * @throws OperationFailedException 
181      * @throws MissingParameterException 
182      * @throws InvalidParameterException 
183      */
184     private void addUpdateRequirementStateNodes(List<String> requirements, String state, BaseDTOAssemblyNode<?, ?> result) throws InvalidParameterException, MissingParameterException, OperationFailedException {
185     	for (String requirementId:requirements){
186     		try {
187 	    		CluInfo requirementClu = luService.getClu(requirementId);
188 	            requirementClu.setState(state);
189 	            BaseDTOAssemblyNode<Object, CluInfo> reqCluNode = new BaseDTOAssemblyNode<Object, CluInfo>(null);
190 	            reqCluNode.setNodeData(requirementClu);
191 	            reqCluNode.setOperation(NodeOperation.UPDATE);
192 	            result.getChildNodes().add(reqCluNode);
193     		} catch (DoesNotExistException e){
194     			//Don't need to update what doesn't exist
195     		}
196     	}
197 	}
198 
199 	/**
200      * Copy identifier values from clu to program
201      *
202      * @param clu
203      * @param o
204      * @return
205      * @throws AssemblyException
206      */
207     public ProgramIdentifierAssembly assembleIdentifiers(CluInfo clu, ProgramIdentifierAssembly program) throws AssemblyException {
208 
209         if (clu.getOfficialIdentifier() != null) {
210             if (clu.getOfficialIdentifier().getShortName() != null) {
211                 program.setShortTitle(clu.getOfficialIdentifier().getShortName());
212             }
213             if (clu.getOfficialIdentifier().getLongName() != null) {
214                 program.setLongTitle(clu.getOfficialIdentifier().getLongName());
215             }
216             if (clu.getOfficialIdentifier().getCode() != null) {
217                 program.setCode(clu.getOfficialIdentifier().getCode());
218             }
219         }
220         if (clu.getAlternateIdentifiers() != null) {
221             for (CluIdentifierInfo cluIdInfo : clu.getAlternateIdentifiers()) {
222                 String idInfoType = cluIdInfo.getType();
223                 if (ProgramAssemblerConstants.TRANSCRIPT.equals(idInfoType)) {
224                     program.setTranscriptTitle(cluIdInfo.getShortName());
225                 } else if (ProgramAssemblerConstants.DIPLOMA.equals(idInfoType)) {
226                     program.setDiplomaTitle(cluIdInfo.getShortName());
227                 }
228             }
229         }
230         return program;
231     }
232 
233 
234     /**
235      * Copy identifier values from program to clu
236      *
237      * @param clu
238      * @param program
239      * @param operation
240      * @return
241      * @throws AssemblyException
242      */
243     public CluInfo disassembleIdentifiers(CluInfo clu, ProgramIdentifierAssembly program, NodeOperation operation) throws AssemblyException {
244 
245         CluIdentifierInfo official = null != clu.getOfficialIdentifier() ? clu.getOfficialIdentifier() : new CluIdentifierInfo();
246 
247         official.setCode(program.getCode());
248         official.setLongName(program.getLongTitle());
249         official.setShortName(program.getShortTitle());
250         official.setState(program.getState());
251         // gotta be this type
252         official.setType(ProgramAssemblerConstants.OFFICIAL);
253 
254         if (program instanceof CredentialProgramInfo) {
255             CredentialProgramInfo cred = (CredentialProgramInfo)program;
256             official.setLevel(cred.getProgramLevel());
257         }
258 
259         clu.setOfficialIdentifier(official);
260 
261         //Remove any existing diploma or transcript alt identifiers
262         CluIdentifierInfo diplomaInfo = null;
263         CluIdentifierInfo transcriptInfo = null;
264         for(Iterator<CluIdentifierInfo> iter = clu.getAlternateIdentifiers().iterator();iter.hasNext();){
265             CluIdentifierInfo cluIdentifier = iter.next();
266             if (ProgramAssemblerConstants.DIPLOMA.equals(cluIdentifier.getType())) {
267                 diplomaInfo = cluIdentifier;
268                 diplomaInfo.setState(program.getState());
269             } else if (ProgramAssemblerConstants.TRANSCRIPT.equals(cluIdentifier.getType())) {
270                 transcriptInfo = cluIdentifier;
271                 transcriptInfo.setState(program.getState());
272             }
273         }
274 
275         if (program.getDiplomaTitle() != null) {
276             if (diplomaInfo == null) {
277                 diplomaInfo = new CluIdentifierInfo();
278                 diplomaInfo.setState(program.getState());
279                 clu.getAlternateIdentifiers().add(diplomaInfo);
280             }
281             diplomaInfo.setCode(official.getCode());
282             diplomaInfo.setShortName(program.getDiplomaTitle());
283             diplomaInfo.setType(ProgramAssemblerConstants.DIPLOMA);
284         }
285 
286         if (program.getTranscriptTitle() != null) {
287             if (transcriptInfo == null) {
288                 transcriptInfo = new CluIdentifierInfo();
289                 transcriptInfo.setState(program.getState());
290                 clu.getAlternateIdentifiers().add(transcriptInfo);
291             }
292             transcriptInfo.setCode(official.getCode());
293             transcriptInfo.setShortName(program.getTranscriptTitle());
294             transcriptInfo.setType(ProgramAssemblerConstants.TRANSCRIPT);
295         }
296         return clu;
297     }
298 
299     /**
300      * Copy Lu Codes from clu to program
301      *
302      * @param clu
303      * @param program
304      * @return
305      * @throws AssemblyException
306      */
307     public ProgramCodeAssembly assembleLuCodes(CluInfo clu, ProgramCodeAssembly program) throws AssemblyException {
308 
309         if (clu.getLuCodes() != null) {
310             for (LuCodeInfo codeInfo : clu.getLuCodes()) {
311                 if (ProgramAssemblerConstants.CIP_2000.equals(codeInfo.getType())) {
312                     program.setCip2000Code(codeInfo.getValue());
313                 } else if (ProgramAssemblerConstants.CIP_2010.equals(codeInfo.getType())) {
314                     program.setCip2010Code(codeInfo.getValue());
315                 } else if (ProgramAssemblerConstants.HEGIS.equals(codeInfo.getType())) {
316                     program.setHegisCode(codeInfo.getValue());
317                 } else if (ProgramAssemblerConstants.UNIVERSITY_CLASSIFICATION.equals(codeInfo.getType())) {
318                     program.setUniversityClassification(codeInfo.getValue());
319                 } else if (ProgramAssemblerConstants.SELECTIVE_ENROLLMENT.equals(codeInfo.getType())) {
320                     program.setSelectiveEnrollmentCode(codeInfo.getValue());
321                 }
322             }
323         }
324 
325         return program;
326     }
327 
328 
329     /**
330      * Copy Lu Codes from program to clu
331      *
332      * @param clu
333      * @param program
334      * @param operation
335      * @throws AssemblyException
336      */
337     public CluInfo disassembleLuCodes(CluInfo clu, ProgramCodeAssembly program, NodeOperation operation) throws AssemblyException {
338 
339         clu.setLuCodes(new ArrayList<LuCodeInfo>());
340 
341         addLuCodeFromProgram(ProgramAssemblerConstants.CIP_2000, program.getCip2000Code(), clu.getLuCodes());
342         addLuCodeFromProgram(ProgramAssemblerConstants.CIP_2010, program.getCip2010Code(), clu.getLuCodes());
343         addLuCodeFromProgram(ProgramAssemblerConstants.HEGIS, program.getHegisCode(), clu.getLuCodes());
344         addLuCodeFromProgram(ProgramAssemblerConstants.UNIVERSITY_CLASSIFICATION, program.getUniversityClassification(), clu.getLuCodes());
345         addLuCodeFromProgram(ProgramAssemblerConstants.SELECTIVE_ENROLLMENT, program.getSelectiveEnrollmentCode(), clu.getLuCodes());
346 
347         return clu;
348 
349     }
350 
351     /**
352      * Copy AdminOrg id's from clu's AdminOrgInfo's to program
353      *
354      * @param clu
355      * @param program
356      * @return
357      * @throws AssemblyException
358      */
359     public ProgramBasicOrgAssembly assembleBasicAdminOrgs(CluInfo clu, ProgramBasicOrgAssembly program) throws AssemblyException {
360 
361         if (clu.getAdminOrgs() != null) {
362             clearProgramAdminOrgs(program);
363             for (AdminOrgInfo cluOrg : clu.getAdminOrgs()) {
364                 if (cluOrg.getType().equals(ProgramAssemblerConstants.CURRICULUM_OVERSIGHT_DIVISION)) {
365                     program.getDivisionsContentOwner().add(cluOrg.getOrgId());
366                 }
367                 else if (cluOrg.getType().equals(ProgramAssemblerConstants.STUDENT_OVERSIGHT_DIVISION)) {
368                     program.getDivisionsStudentOversight().add(cluOrg.getOrgId())  ;
369                 }
370                 else if (cluOrg.getType().equals(ProgramAssemblerConstants.CURRICULUM_OVERSIGHT_UNIT)) {
371                     program.getUnitsContentOwner().add(cluOrg.getOrgId())  ;
372                 }
373                 else if (cluOrg.getType().equals(ProgramAssemblerConstants.STUDENT_OVERSIGHT_UNIT)) {
374                     program.getUnitsStudentOversight().add(cluOrg.getOrgId())  ;
375                 }
376             }
377         }
378         return program;
379     }
380 
381     public ProgramFullOrgAssembly assembleFullOrgs(CluInfo clu, ProgramFullOrgAssembly program) throws AssemblyException {
382 
383         clearFullAdminOrgs(program);
384         for (AdminOrgInfo cluOrg : clu.getAdminOrgs()) {
385             if (cluOrg.getType().equals(ProgramAssemblerConstants.DEPLOYMENT_DIVISION)) {
386                 program.getDivisionsDeployment().add(cluOrg.getOrgId())  ;
387             }
388             else if (cluOrg.getType().equals(ProgramAssemblerConstants.FINANCIAL_RESOURCES_DIVISION)) {
389                 program.getDivisionsFinancialResources().add(cluOrg.getOrgId())  ;
390             }
391             else if (cluOrg.getType().equals(ProgramAssemblerConstants.FINANCIAL_CONTROL_DIVISION)) {
392                 program.getDivisionsFinancialControl().add(cluOrg.getOrgId())  ;
393             }
394             else if (cluOrg.getType().equals(ProgramAssemblerConstants.DEPLOYMENT_UNIT)) {
395                 program.getUnitsDeployment().add(cluOrg.getOrgId())  ;
396             }
397             else if (cluOrg.getType().equals(ProgramAssemblerConstants.FINANCIAL_RESOURCES_UNIT)) {
398                 program.getUnitsFinancialResources().add(cluOrg.getOrgId())  ;
399             }
400             else if (cluOrg.getType().equals(ProgramAssemblerConstants.FINANCIAL_CONTROL_UNIT)) {
401                 program.getUnitsFinancialControl().add(cluOrg.getOrgId())  ;
402             }
403         }
404         return program;
405     }
406 
407     private void clearProgramAdminOrgs(ProgramBasicOrgAssembly program) {
408         program.setDivisionsContentOwner(new ArrayList<String>());
409         program.setDivisionsStudentOversight(new ArrayList<String>());
410         program.setUnitsContentOwner(new ArrayList<String>());
411         program.setUnitsStudentOversight(new ArrayList<String>());
412     }
413 
414     private void clearFullAdminOrgs(ProgramFullOrgAssembly program) {
415         program.setDivisionsDeployment(new ArrayList<String>());
416         program.setDivisionsFinancialResources(new ArrayList<String>());
417         program.setDivisionsFinancialControl(new ArrayList<String>());
418         program.setUnitsDeployment(new ArrayList<String>());
419         program.setUnitsFinancialResources(new ArrayList<String>());
420         program.setUnitsFinancialControl(new ArrayList<String>());
421     }
422 
423     /**
424      * Copy AdminOrg values from program to clu
425      *
426      * @param clu
427      * @param o
428      * @param operation
429      */
430     public CluInfo disassembleAdminOrgs(CluInfo clu, ProgramBasicOrgAssembly program, NodeOperation operation){
431 
432         // clear out all old admin orgs
433         clu.setAdminOrgs(new ArrayList<AdminOrgInfo>());
434 
435         newBuildAdminOrgs(clu,  program.getDivisionsContentOwner(), ProgramAssemblerConstants.CURRICULUM_OVERSIGHT_DIVISION);
436         newBuildAdminOrgs(clu, program.getDivisionsStudentOversight(), ProgramAssemblerConstants.STUDENT_OVERSIGHT_DIVISION );
437         newBuildAdminOrgs(clu,  program.getUnitsContentOwner(), ProgramAssemblerConstants.CURRICULUM_OVERSIGHT_UNIT);
438         newBuildAdminOrgs(clu, program.getUnitsStudentOversight(), ProgramAssemblerConstants.STUDENT_OVERSIGHT_UNIT );
439         if (program instanceof CredentialProgramInfo) {
440             List<String> institutionOrgs = new ArrayList<String>();
441             institutionOrgs.add(((CredentialProgramInfo)program).getInstitution().getOrgId());
442             newBuildAdminOrgs(clu, institutionOrgs , ProgramAssemblerConstants.INSTITUTION) ;
443         }
444         if (program instanceof ProgramFullOrgAssembly) {
445             ProgramFullOrgAssembly fullOrg = (ProgramFullOrgAssembly) program;
446             newBuildAdminOrgs(clu, fullOrg.getDivisionsDeployment(), ProgramAssemblerConstants.DEPLOYMENT_DIVISION);
447             newBuildAdminOrgs(clu, fullOrg.getDivisionsFinancialResources(), ProgramAssemblerConstants.FINANCIAL_RESOURCES_DIVISION);
448             newBuildAdminOrgs(clu, fullOrg.getDivisionsFinancialControl(), ProgramAssemblerConstants.FINANCIAL_CONTROL_DIVISION);
449             newBuildAdminOrgs(clu, fullOrg.getUnitsDeployment(), ProgramAssemblerConstants.DEPLOYMENT_UNIT);
450             newBuildAdminOrgs(clu, fullOrg.getUnitsFinancialResources(), ProgramAssemblerConstants.FINANCIAL_RESOURCES_UNIT);
451             newBuildAdminOrgs(clu, fullOrg.getUnitsFinancialControl(), ProgramAssemblerConstants.FINANCIAL_CONTROL_UNIT);
452 
453         }
454         return clu;
455 
456     }
457 
458     private CluInfo newBuildAdminOrgs(CluInfo clu,  List<String> orgIds, String type) {
459 
460         if (null != orgIds) {
461             for (String orgId : orgIds) {
462                 AdminOrgInfo subjectOrg = new AdminOrgInfo();
463                 subjectOrg.setType(type);
464                 subjectOrg.setOrgId(orgId);
465                 clu.getAdminOrgs().add(subjectOrg);
466             }
467         }
468         return clu;
469     }
470 
471     /**
472      * Copy result option values from clu to program
473      *
474      * @param cluId
475      * @param resultType
476      * @return
477      * @throws AssemblyException
478      */
479     public List<String> assembleResultOptions(String cluId) throws AssemblyException {
480         List<String> resultOptions = null;
481         try{
482             List<CluResultInfo> cluResults = luService.getCluResultByClu(cluId);
483 
484             List<String> resultTypes = new ArrayList<String>();
485             resultTypes.add(ProgramAssemblerConstants.DEGREE_RESULTS);
486             resultTypes.add(ProgramAssemblerConstants.CERTIFICATE_RESULTS);
487 
488             resultOptions = cluAssemblerUtils.assembleCluResults(resultTypes, cluResults);
489 
490         } catch (DoesNotExistException e){
491         } catch (Exception e) {
492             throw new AssemblyException("Error getting major results", e);
493         }
494         return resultOptions;
495     }
496 
497     /**
498      * Copy atp values  from clu to program
499      *
500      * @param clu
501      * @param program
502      * @return
503      * @throws AssemblyException
504      */
505     public ProgramAtpAssembly assembleAtps(CluInfo clu, ProgramAtpAssembly program) throws AssemblyException {
506 
507         if (clu.getExpectedFirstAtp() != null) {
508             program.setStartTerm(clu.getExpectedFirstAtp());
509         }
510         if (clu.getLastAtp() != null) {
511             program.setEndTerm(clu.getLastAtp());
512         }
513         if (clu.getLastAdmitAtp() != null) {
514             program.setEndProgramEntryTerm(clu.getLastAdmitAtp());
515         }
516         return program;
517     }
518 
519 
520     /**
521      * Copy atp values from Program to clu
522      *
523      * @param clu
524      * @param program
525      * @return
526      * @throws AssemblyException
527      */
528     public CluInfo disassembleAtps(CluInfo clu, ProgramAtpAssembly program, NodeOperation operation) throws AssemblyException {
529 
530         clu.setExpectedFirstAtp(program.getStartTerm());
531         clu.setLastAtp(program.getEndTerm());
532         clu.setLastAdmitAtp(program.getEndProgramEntryTerm());
533 
534         return clu;
535 
536     }
537 
538     /**
539      * Copy publication values from clu to program
540      *
541      * @param clu
542      * @param program
543      * @return
544      * @throws AssemblyException
545      */
546     public ProgramPublicationAssembly assemblePublications(CluInfo clu, ProgramPublicationAssembly program) throws AssemblyException {
547 
548 
549         if (clu.getReferenceURL() != null) {
550             program.setReferenceURL(clu.getReferenceURL());
551         }
552 
553         try {
554             List<CluPublicationInfo> cluPublications = luService.getCluPublicationsByCluId(clu.getId());
555 
556             List<String> targets = new ArrayList<String>();
557 
558             for (CluPublicationInfo cluPublication : cluPublications) {
559                 if (cluPublication.getType().equals(ProgramAssemblerConstants.CATALOG)) {
560                     assembleCatalogDescr(program, cluPublication);
561                 }
562                 else {
563                     targets.add(cluPublication.getType());
564                 }
565             }
566 
567             if (targets != null && !targets.isEmpty()) {
568                 program.setCatalogPublicationTargets(targets);
569             }
570         } catch (DoesNotExistException e) {
571         } catch (InvalidParameterException e) {
572         } catch (MissingParameterException e) {
573         } catch (OperationFailedException e) {
574             throw new AssemblyException("Error getting publication targets", e);
575         }
576         return program;
577     }
578 
579     private void assembleCatalogDescr(ProgramPublicationAssembly program, CluPublicationInfo cluPublication)  {
580 
581         for (FieldInfo fieldInfo : cluPublication.getVariants()) {
582             if (fieldInfo.getId().equals(ProgramAssemblerConstants.CATALOG_DESCR)) {
583                 RichTextInfo desc = new RichTextInfo();
584                 desc.setPlain(fieldInfo.getValue());
585                 desc.setFormatted(fieldInfo.getValue());
586                 program.setCatalogDescr(desc);
587                 break;
588             }
589         }
590     }
591 
592      private List<BaseDTOAssemblyNode<?, ?>> disassembleCatalogDescr(ProgramPublicationAssembly program,  NodeOperation operation) throws AssemblyException {
593 
594          List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
595 
596          CluPublicationInfo currentPubInfo = null;
597 
598          try {
599 
600              // if not create get current catalog descr
601              if (!NodeOperation.CREATE.equals(operation)) {
602                  List<CluPublicationInfo> pubs = luService.getCluPublicationsByCluId(program.getId());
603                  for (CluPublicationInfo pubInfo : pubs) {
604                      if (pubInfo.getType().equals(ProgramAssemblerConstants.CATALOG)) {
605                          currentPubInfo = pubInfo;
606                      }
607                  }
608              }
609 
610              if (program.getCatalogDescr() != null) {
611                  //  If this is a create then create new catalog descr
612                  if (NodeOperation.CREATE == operation
613                          || (NodeOperation.UPDATE == operation && currentPubInfo == null )) {
614                      // the description does not exist, so create
615                      CluPublicationInfo pubInfo = buildCluPublicationInfo(program.getId(), ProgramAssemblerConstants.CATALOG);
616                      pubInfo.setState(program.getState());
617                      FieldInfo variant = new FieldInfo();
618                      variant.setId(ProgramAssemblerConstants.CATALOG_DESCR);
619                      variant.setValue(program.getCatalogDescr() .getPlain());
620                      pubInfo.getVariants().add(variant);
621 
622                      BaseDTOAssemblyNode<Object, CluPublicationInfo> pubNode = new BaseDTOAssemblyNode<Object, CluPublicationInfo>(
623                              null);
624                      pubNode.setNodeData(pubInfo);
625                      pubNode.setOperation(NodeOperation.CREATE);
626 
627                      results.add(pubNode);
628                  } else if (NodeOperation.UPDATE == operation
629                          && currentPubInfo != null) {
630 
631                      CluPublicationInfo pubInfo = currentPubInfo;
632                      pubInfo.setState(program.getState());
633                      for (FieldInfo fieldInfo : pubInfo.getVariants()) {
634                          if (fieldInfo.getId().equals(ProgramAssemblerConstants.CATALOG_DESCR)) {
635                              fieldInfo.setValue(program.getCatalogDescr() .getPlain());
636                              break;
637                          }
638                      }
639 
640                      BaseDTOAssemblyNode<Object, CluPublicationInfo> pubNode = new BaseDTOAssemblyNode<Object, CluPublicationInfo>(
641                              null);
642                      pubNode.setNodeData(pubInfo);
643                      pubNode.setOperation(NodeOperation.UPDATE);
644 
645                      results.add(pubNode);
646                      
647                  }
648                  else if (NodeOperation.DELETE == operation  )  {
649 
650                      deletePublicationInfo(results, currentPubInfo);
651                  }
652              }
653          } catch (Exception e) {
654              throw new AssemblyException(e);
655          }
656          return results;
657     }
658 
659     private void deletePublicationInfo(List<BaseDTOAssemblyNode<?, ?>> results, CluPublicationInfo currentPubInfo) {
660         CluPublicationInfo descrToDelete = new CluPublicationInfo();
661         descrToDelete.setId(currentPubInfo.getId());
662         BaseDTOAssemblyNode<Object, CluPublicationInfo> pubToDeleteNode = new BaseDTOAssemblyNode<Object, CluPublicationInfo>(
663                 null);
664         pubToDeleteNode.setNodeData(descrToDelete);
665         pubToDeleteNode.setOperation(NodeOperation.DELETE);
666         results.add(pubToDeleteNode);
667     }
668 
669     /**
670      * Copy publication values from program to clu
671      *
672      * @param clu
673      * @param program
674      * @param operation
675      * @return
676      * @throws AssemblyException
677      */
678     public CluInfo disassemblePublications(CluInfo clu, ProgramPublicationAssembly program, NodeOperation operation, BaseDTOAssemblyNode<?, ?> result) throws AssemblyException {
679 
680         clu.setReferenceURL(program.getReferenceURL());
681         clu.setState(program.getState());
682 
683         List<BaseDTOAssemblyNode<?, ?>> targetResults = disassemblePublicationTargets(program, operation);
684         if (targetResults != null && targetResults.size()> 0) {
685             result.getChildNodes().addAll(targetResults);
686         }
687 
688         List<BaseDTOAssemblyNode<?, ?>> descrResults = disassembleCatalogDescr(program, operation) ;
689         if (descrResults != null && descrResults.size()> 0) {
690             result.getChildNodes().addAll(descrResults);
691         }
692 
693         return clu;
694 
695     }
696 
697     /**
698      * Copy credential program id value from program to clu
699      *
700      * @param clu
701      * @param o
702      * @param operation
703      * @return
704      * @throws AssemblyException
705      */
706     public List<BaseDTOAssemblyNode<?,?>>  disassembleCredentialProgram(ProgramCredentialAssembly program, NodeOperation operation, String relationType) throws AssemblyException {
707 
708         List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
709 
710         try {
711             CluInfo credentialClu = luService.getClu(program.getCredentialProgramId());
712         } catch (DoesNotExistException e) {
713         } catch (Exception e) {
714             throw new AssemblyException("Credential Clu does not exist for " + program.getCredentialProgramId());
715         }
716 
717         Map<String, String> currentRelations = new HashMap<String, String>();
718 
719         if (!NodeOperation.CREATE.equals(operation)) {
720             try {
721                 List<CluCluRelationInfo> cluRelations = luService.getCluCluRelationsByClu(program.getId());
722                 for (CluCluRelationInfo cluRelation : cluRelations) {
723                     if (relationType.equals(cluRelation.getType()) ) {
724                         currentRelations.put(cluRelation.getRelatedCluId(), cluRelation.getId());
725                     }
726                 }
727             } catch (DoesNotExistException e) {
728             } catch (InvalidParameterException e) {
729             } catch (MissingParameterException e) {
730             } catch (OperationFailedException e) {
731                 throw new AssemblyException("Error getting related clus", e);
732             }
733         }
734 
735 
736         //  If this is a create then vreate new relation
737         if (NodeOperation.CREATE == operation
738                 || (NodeOperation.UPDATE == operation && !currentRelations.containsKey(program.getCredentialProgramId()) )) {
739             // the relation does not exist, so create
740             CluCluRelationInfo relation = new CluCluRelationInfo();
741             relation.setCluId(program.getCredentialProgramId());
742             relation.setRelatedCluId(program.getId());
743             relation.setType(relationType);
744             // We are hard coding this to active since relations can only be active/inactive
745             // DO NOT propagate states such as DRAFT etc to the relations.
746             relation.setState(DtoConstants.STATE_ACTIVE);
747 
748             BaseDTOAssemblyNode<Object, CluCluRelationInfo> relationNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
749                     null);
750             relationNode.setNodeData(relation);
751             relationNode.setOperation(NodeOperation.CREATE);
752 
753             results.add(relationNode);
754         } else if (NodeOperation.UPDATE == operation
755                 && currentRelations.containsKey(program.getCredentialProgramId())) {
756             // If the relationship already exists update it
757 
758             // remove this entry from the map so we can tell what needs to
759             // be deleted at the end
760             currentRelations.remove(program.getCredentialProgramId());
761         } else if (NodeOperation.DELETE == operation
762                 && currentRelations.containsKey(program.getId()))  {
763             // Delete the Format and its relation
764             CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
765             relationToDelete.setId( currentRelations.get(program.getId()) );
766             BaseDTOAssemblyNode<Object, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
767                     null);
768             relationToDeleteNode.setNodeData(relationToDelete);
769             relationToDeleteNode.setOperation(NodeOperation.DELETE);
770             results.add(relationToDeleteNode);
771 
772             // remove this entry from the map so we can tell what needs to
773             // be deleted at the end
774             currentRelations.remove(program.getId());
775         }
776 
777         if(currentRelations != null && currentRelations.size() > 0){
778 	        for (Map.Entry<String, String> entry : currentRelations.entrySet()) {
779 	            // Create a new relation with the id of the relation we want to
780 	            // delete
781 	            CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
782 	            relationToDelete.setId( entry.getValue() );
783 	            BaseDTOAssemblyNode<Object, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
784 	                    null);
785 	            relationToDeleteNode.setNodeData(relationToDelete);
786 	            relationToDeleteNode.setOperation(NodeOperation.DELETE);
787 	            results.add(relationToDeleteNode);
788 	        }
789         }
790         return results;
791     }
792 
793     public List<BaseDTOAssemblyNode<?, ?>> addRelationNodes(String cluId, String relatedCluId, String relationType, NodeOperation operation)throws AssemblyException{
794     	Map<String, String> currentRelations = null;
795     	List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
796 
797         if (!NodeOperation.CREATE.equals(operation)) {
798         	currentRelations = getCluCluRelations(cluId, relationType);
799         }
800 
801         //  If this is a create then vreate new relation
802         if (NodeOperation.CREATE == operation
803                 || (NodeOperation.UPDATE == operation && !currentRelations.containsKey(relatedCluId) )) {
804             // the relation does not exist, so create
805         	addCreateRelationNode(cluId, relatedCluId, relationType, results);
806         } else if (NodeOperation.UPDATE == operation
807                 && currentRelations.containsKey(relatedCluId)) {
808             // If the relationship already exists update it
809 
810             // remove this entry from the map so we can tell what needs to
811             // be deleted at the end
812             currentRelations.remove(relatedCluId);
813         } else if (NodeOperation.DELETE == operation
814                 && currentRelations.containsKey(relatedCluId))  {
815             // Delete the Format and its relation
816         	addDeleteRelationNodes(currentRelations, results);
817 
818             // remove this entry from the map so we can tell what needs to
819             // be deleted at the end
820             currentRelations.remove(relatedCluId);
821         }
822         
823         if(currentRelations != null && currentRelations.size() > 0){
824 	        for (Map.Entry<String, String> entry : currentRelations.entrySet()) {
825 	            // Create a new relation with the id of the relation we want to
826 	            // delete
827 	            CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
828 	            relationToDelete.setId( entry.getValue() );
829 	            BaseDTOAssemblyNode<Object, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
830 	                    null);
831 	            relationToDeleteNode.setNodeData(relationToDelete);
832 	            relationToDeleteNode.setOperation(NodeOperation.DELETE);
833 	            results.add(relationToDeleteNode);
834 	        }
835         }
836         return results;
837     }
838     public List<BaseDTOAssemblyNode<?, ?>> addAllRelationNodes(String cluId, String relatedCluId, String relationType, NodeOperation operation, Map<String, String> currentRelations)throws AssemblyException{
839     	List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
840 
841 		if (NodeOperation.CREATE == operation
842 		        || (NodeOperation.UPDATE == operation && !currentRelations.containsKey(relatedCluId) )) {
843 		    // the relation does not exist, so create
844 			addCreateRelationNode(cluId, relatedCluId, relationType, results);
845 		} else if (NodeOperation.UPDATE == operation
846 		        && currentRelations.containsKey(relatedCluId)) {
847 		    // If the relationship already exists update it
848 		
849 		    // remove this entry from the map so we can tell what needs to
850 		    // be deleted at the end
851 		    currentRelations.remove(relatedCluId);
852 		} else if (NodeOperation.DELETE == operation
853 		        && currentRelations.containsKey(relatedCluId))  {
854 		    // Delete the Format and its relation
855 			addDeleteRelationNodes(currentRelations, results);
856 		
857 		    // remove this entry from the map so we can tell what needs to
858 		    // be deleted at the end
859 		    currentRelations.remove(relatedCluId);
860 		}
861         
862         return results;
863     }
864     public Map<String, String> getCluCluRelations(String cluId, String relationType) throws AssemblyException{
865         Map<String, String> currentRelations = new HashMap<String, String>();
866 
867             try {
868                 List<CluCluRelationInfo> cluRelations = luService.getCluCluRelationsByClu(cluId);
869                
870                 for (CluCluRelationInfo cluRelation : cluRelations) {
871                     if (relationType.equals(cluRelation.getType())) {
872                         currentRelations.put(cluRelation.getRelatedCluId(), cluRelation.getId());
873                     }
874                 }
875             } catch (DoesNotExistException e) {
876             } catch (InvalidParameterException e) {
877             } catch (MissingParameterException e) {
878             } catch (OperationFailedException e) {
879                 throw new AssemblyException("Error getting related clus", e);
880             }
881 
882             return currentRelations;
883     }
884     
885     public Map<String, CluCluRelationInfo> getCluCluActiveRelations(String cluId, String relationType) throws AssemblyException{
886         Map<String, CluCluRelationInfo> currentRelations = new HashMap<String, CluCluRelationInfo>();
887 
888             try {
889                 List<CluCluRelationInfo> cluRelations = luService.getCluCluRelationsByClu(cluId);
890 
891                 for (CluCluRelationInfo cluRelation : cluRelations) {
892                     if (relationType.equals(cluRelation.getType()) && (!cluRelation.getState().isEmpty() && cluRelation.getState().equalsIgnoreCase(DtoConstants.STATE_ACTIVE))) {
893                         currentRelations.put(cluRelation.getRelatedCluId(), cluRelation);
894                     }
895                 }
896             } catch (DoesNotExistException e) {
897             } catch (InvalidParameterException e) {
898             } catch (MissingParameterException e) {
899             } catch (OperationFailedException e) {
900                 throw new AssemblyException("Error getting related clus", e);
901             }
902 
903             return currentRelations;
904     }
905 
906     public void addCreateRelationNode(String cluId, String relatedCluId, String relationType, List<BaseDTOAssemblyNode<?, ?>> results){
907         CluCluRelationInfo relation = new CluCluRelationInfo();
908         relation.setCluId(cluId);
909         relation.setRelatedCluId(relatedCluId);
910         relation.setType(relationType);
911         
912         // Relations can only be in state Active or Inactive
913         // DO NOT set state on relations to Draft, Approved, etc
914         // We will default to Active
915         relation.setState(DtoConstants.STATE_ACTIVE);
916 
917         BaseDTOAssemblyNode<Object, CluCluRelationInfo> relationNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
918                 null);
919         relationNode.setNodeData(relation);
920         relationNode.setOperation(NodeOperation.CREATE);
921 
922         results.add(relationNode);
923 
924     }
925 
926     public void addDeleteRelationNodes(Map<String, String> currentRelations, List<BaseDTOAssemblyNode<?, ?>> results){
927         for (Map.Entry<String, String> entry : currentRelations.entrySet()) {
928             // Create a new relation with the id of the relation we want to
929             // delete
930             CluCluRelationInfo relationToDelete = new CluCluRelationInfo();
931             relationToDelete.setId( entry.getValue() );
932             BaseDTOAssemblyNode<Object, CluCluRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
933                     null);
934             relationToDeleteNode.setNodeData(relationToDelete);
935             relationToDeleteNode.setOperation(NodeOperation.DELETE);
936             results.add(relationToDeleteNode);
937         }
938     }
939     
940     public void addInactiveRelationNodes(Map<String, CluCluRelationInfo> currentRelations, List<BaseDTOAssemblyNode<?, ?>> results){
941         for (Map.Entry<String, CluCluRelationInfo> entry : currentRelations.entrySet()) {
942             CluCluRelationInfo inactiveRelation = new CluCluRelationInfo();
943             inactiveRelation = entry.getValue();
944             inactiveRelation.setState(DtoConstants.STATE_INACTIVE);
945             BaseDTOAssemblyNode<Object, CluCluRelationInfo> inactiveRelationNode = new BaseDTOAssemblyNode<Object, CluCluRelationInfo>(
946                     null);
947             inactiveRelationNode.setNodeData(inactiveRelation);
948             inactiveRelationNode.setOperation(NodeOperation.UPDATE);
949             results.add(inactiveRelationNode);
950         }
951     }
952 
953     private void addLuCodeFromProgram(String type, String value, List<LuCodeInfo> list) throws AssemblyException {
954 
955         if (value != null && !value.isEmpty()) {
956             LuCodeInfo code = new LuCodeInfo();
957             code.setType(type);
958             code.setValue(value);
959             code.setAttributes(new HashMap<String, String>());
960             list.add(code);
961         }
962     }
963 
964     /**
965      * Copy publications from program to clu
966      *
967      * @param clu
968      * @param o
969      * @param operation
970      * @return
971      * @throws AssemblyException
972           */
973     private List<BaseDTOAssemblyNode<?, ?>> disassemblePublicationTargets(ProgramPublicationAssembly program,  NodeOperation operation) throws AssemblyException {
974 
975         List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
976 
977         Map<String, CluPublicationInfo> currentPubs = new HashMap<String, CluPublicationInfo>();
978         if (!NodeOperation.CREATE.equals(operation)) {
979 
980             // Get the current publications and put them in a map
981             try {
982                 List<CluPublicationInfo> cluPubs = luService.getCluPublicationsByCluId(program.getId());
983                 for(CluPublicationInfo cluPub : cluPubs){
984                     cluPub.setState(program.getState());
985                     if (!cluPub.getType().equals(ProgramAssemblerConstants.CATALOG)) {
986                         currentPubs.put(cluPub.getType(), cluPub);                        
987                     }
988                 }
989             } catch (DoesNotExistException e) {
990             } catch (Exception e) {
991                 throw new AssemblyException("Error finding publications");
992             }
993         }
994 
995         if (program.getCatalogPublicationTargets() != null && !program.getCatalogPublicationTargets().isEmpty()) {
996             for (String publicationType : program.getCatalogPublicationTargets()) {
997                 //  If this is a create then create new publication
998                 if (NodeOperation.CREATE == operation
999                         || (NodeOperation.UPDATE == operation && !currentPubs.containsKey(publicationType) )) {
1000                     // the publication does not exist, so create
1001                     CluPublicationInfo pubInfo = buildCluPublicationInfo(program.getId(), publicationType);
1002                     
1003                     // Set the publication type state to match the program state.
1004                     pubInfo.setState(program.getState());
1005                     BaseDTOAssemblyNode<Object, CluPublicationInfo> pubNode = new BaseDTOAssemblyNode<Object, CluPublicationInfo>(
1006                             null);
1007                     pubNode.setNodeData(pubInfo);
1008                     pubNode.setOperation(NodeOperation.CREATE);
1009 
1010                     results.add(pubNode);
1011                 } else if (NodeOperation.UPDATE == operation
1012                         && currentPubs.containsKey(publicationType)) {
1013                     // Update the state of an existing pub info to the
1014                     // program state. To do this we need to remove the publication
1015                     // type and create a new node with the update operation
1016                     CluPublicationInfo pubInfo = currentPubs.remove(publicationType);
1017                     pubInfo.setState(program.getState());
1018                     BaseDTOAssemblyNode<Object, CluPublicationInfo> pubNode = new BaseDTOAssemblyNode<Object, CluPublicationInfo>(
1019                                null);
1020                     pubNode.setNodeData(pubInfo);
1021                     pubNode.setOperation(NodeOperation.UPDATE);
1022                     results.add(pubNode);
1023                  } else if (NodeOperation.DELETE == operation
1024                         && currentPubs.containsKey(publicationType))  {
1025 
1026                     CluPublicationInfo pubToDelete = new CluPublicationInfo();
1027                     pubToDelete.setId(publicationType);
1028                     BaseDTOAssemblyNode<Object, CluPublicationInfo> pubToDeleteNode = new BaseDTOAssemblyNode<Object, CluPublicationInfo>(
1029                             null);
1030                     pubToDeleteNode.setNodeData(pubToDelete);
1031                     pubToDeleteNode.setOperation(NodeOperation.DELETE);
1032                     results.add(pubToDeleteNode);
1033 
1034                     currentPubs.remove(publicationType);
1035                 }
1036             }
1037         }
1038 
1039 
1040         for (Map.Entry<String, CluPublicationInfo> entry : currentPubs.entrySet()) {
1041             // Create a new relation with the id of the relation we want to
1042             // delete
1043             deletePublicationInfo(results, entry.getValue());
1044         }
1045 
1046         return results;
1047     }
1048 
1049     private CluPublicationInfo buildCluPublicationInfo(String programId, String publicationType) throws AssemblyException {
1050 
1051         CluPublicationInfo pubInfo = new CluPublicationInfo();
1052         pubInfo.setType(publicationType);
1053         pubInfo.setCluId(programId);
1054 
1055         return pubInfo;
1056     }
1057 
1058     // Spring setters
1059     public void setLuService(LuService luService) {
1060         this.luService = luService;
1061     }
1062 
1063     public void setCluAssemblerUtils(CluAssemblerUtils cluAssemblerUtils) {
1064         this.cluAssemblerUtils = cluAssemblerUtils;
1065     }
1066 
1067     public String getCredentialProgramID(String cluId) throws AssemblyException {
1068 
1069         List<String> credentialProgramIDs = null;
1070         try {
1071             credentialProgramIDs = luService.getCluIdsByRelation(cluId, ProgramAssemblerConstants.HAS_MAJOR_PROGRAM);
1072         } catch (Exception e) {
1073             throw new AssemblyException(e);
1074         }
1075         // Can a Program have more than one Credential Program?
1076         // TODO - do we need to validate that?
1077         if (null == credentialProgramIDs || credentialProgramIDs.isEmpty()) {
1078             throw new AssemblyException("Program with ID == " + cluId + " has no Credential Program associated with it.");
1079         } else if (credentialProgramIDs.size() > 1) {
1080             throw new AssemblyException("Program with ID == " + cluId + " has more than one Credential Program associated with it.");
1081         }
1082         return credentialProgramIDs.get(0);
1083     }
1084    
1085 }