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