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