View Javadoc

1   package org.kuali.student.r2.lum.program.service.impl;
2   
3   import org.apache.log4j.Logger;
4   import org.kuali.student.common.conversion.util.R1R2ConverterUtil;
5   import org.kuali.student.r1.common.assembly.BOAssembler;
6   import org.kuali.student.r1.common.assembly.BaseDTOAssemblyNode;
7   import org.kuali.student.r1.common.assembly.BaseDTOAssemblyNode.NodeOperation;
8   import org.kuali.student.r1.common.assembly.BusinessServiceMethodInvoker;
9   import org.kuali.student.r1.common.dictionary.dto.DataType;
10  import org.kuali.student.r1.common.dictionary.dto.ObjectStructureDefinition;
11  import org.kuali.student.r1.common.dictionary.service.DictionaryService;
12  import org.kuali.student.r1.common.validator.ServerDateParser;
13  import org.kuali.student.r1.common.validator.ValidatorUtils;
14  import org.kuali.student.r1.core.statement.dto.ReqCompFieldInfo;
15  import org.kuali.student.r1.core.statement.dto.ReqComponentInfo;
16  import org.kuali.student.r1.core.statement.dto.StatementTreeViewInfo;
17  import org.kuali.student.r1.lum.statement.typekey.ReqComponentFieldTypes;
18  import org.kuali.student.r2.common.assembler.AssemblyException;
19  import org.kuali.student.r2.common.dto.ContextInfo;
20  import org.kuali.student.r2.common.dto.DtoConstants;
21  import org.kuali.student.r2.common.dto.StatusInfo;
22  import org.kuali.student.r2.common.dto.ValidationResultInfo;
23  import org.kuali.student.r2.common.exceptions.*;
24  import org.kuali.student.r2.core.search.dto.SearchRequestInfo;
25  import org.kuali.student.r2.core.search.dto.SearchResultInfo;
26  import org.kuali.student.r2.core.search.service.SearchManager;
27  import org.kuali.student.r2.common.util.constants.ProgramServiceConstants;
28  import org.kuali.student.r2.common.validator.Validator;
29  import org.kuali.student.r2.common.validator.ValidatorFactory;
30  import org.kuali.student.r2.core.atp.dto.AtpInfo;
31  import org.kuali.student.r2.core.atp.service.AtpService;
32  import org.kuali.student.r2.core.class1.type.dto.TypeInfo;
33  import org.kuali.student.r2.core.versionmanagement.dto.VersionDisplayInfo;
34  import org.kuali.student.r2.lum.clu.dto.CluCluRelationInfo;
35  import org.kuali.student.r2.lum.clu.dto.CluInfo;
36  import org.kuali.student.r2.lum.clu.dto.CluSetInfo;
37  import org.kuali.student.r2.lum.clu.service.CluService;
38  import org.kuali.student.r2.lum.course.dto.LoDisplayInfo;
39  import org.kuali.student.r2.lum.course.infc.LoDisplay;
40  import org.kuali.student.r2.lum.course.service.impl.CourseServiceUtils;
41  import org.kuali.student.r2.lum.program.dto.*;
42  import org.kuali.student.r2.lum.program.service.ProgramService;
43  import org.kuali.student.r2.lum.program.service.assembler.CoreProgramAssembler;
44  import org.kuali.student.r2.lum.program.service.assembler.CredentialProgramAssembler;
45  import org.kuali.student.r2.lum.program.service.assembler.MajorDisciplineAssembler;
46  import org.kuali.student.r2.lum.program.service.assembler.ProgramAssemblerConstants;
47  import org.kuali.student.r2.lum.util.constants.CluServiceConstants;
48  import org.springframework.transaction.annotation.Transactional;
49  
50  import javax.jws.WebParam;
51  import java.util.ArrayList;
52  import java.util.Date;
53  import java.util.Iterator;
54  import java.util.List;
55  import org.kuali.student.r2.core.document.dto.RefDocRelationInfo;
56  import org.kuali.student.r2.core.document.service.DocumentService;
57  
58  public class ProgramServiceImpl implements ProgramService{
59  	final static Logger LOG = Logger.getLogger(ProgramServiceImpl.class);
60  
61      private CluService cluService;
62      private ValidatorFactory validatorFactory;
63      private BusinessServiceMethodInvoker programServiceMethodInvoker;
64      private DictionaryService dictionaryService;
65      private SearchManager searchManager;
66      private MajorDisciplineAssembler majorDisciplineAssembler;
67      private ProgramRequirementAssembler programRequirementAssembler;
68      private CredentialProgramAssembler credentialProgramAssembler;
69      private CoreProgramAssembler coreProgramAssembler;
70  //    private StatementService statementService;
71      private AtpService atpService;
72      private DocumentService documentService;
73      
74  
75      @Override
76      @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
77  	public CredentialProgramInfo createCredentialProgram( String credentialProgramTypeKey,
78                                                            CredentialProgramInfo credentialProgramInfo,
79                                                            ContextInfo contextInfo) throws AlreadyExistsException,
80              DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
81  
82          checkForMissingParameter(credentialProgramInfo, "CredentialProgramInfo");
83  
84          // Validate
85          List<ValidationResultInfo> validationResults = validateCredentialProgram("OBJECT", credentialProgramInfo,contextInfo);
86          if (ValidatorUtils.hasErrors(validationResults)) {
87              throw new DataValidationErrorException("Validation error!", validationResults);
88          }
89  
90          try {
91              return processCredentialProgramInfo(credentialProgramInfo, NodeOperation.CREATE,contextInfo);
92          } catch (AssemblyException e) {
93              LOG.error("Error disassembling Credential Program", e);
94              throw new OperationFailedException("Error disassembling Credential Program");
95          }
96      }
97  
98      @Override
99      @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
100     public HonorsProgramInfo createHonorsProgram(String honorsProgramTypeKey,  HonorsProgramInfo honorsProgramInfo,
101                                                  ContextInfo contextInfo) throws AlreadyExistsException, DataValidationErrorException, InvalidParameterException, MissingParameterException,
102             OperationFailedException, PermissionDeniedException {
103         throw new UnsupportedOperationException("createHonorsProgram");
104         //return null;
105     }
106 
107     @Override
108     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
109     public ProgramRequirementInfo createProgramRequirement( String programRequirementTypeKey,
110                                                             ProgramRequirementInfo programRequirementInfo,  ContextInfo contextInfo) throws AlreadyExistsException,
111             DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
112         checkForMissingParameter(programRequirementInfo, "programRequirementInfo");
113 
114         // Validate
115         List<ValidationResultInfo> validationResults = validateProgramRequirement("OBJECT", programRequirementInfo,contextInfo);
116         if (ValidatorUtils.hasErrors(validationResults)) {
117         	throw new DataValidationErrorException("Validation error!", validationResults);
118         }
119 
120         try {
121             return processProgramRequirement(programRequirementInfo, NodeOperation.CREATE,contextInfo);
122         } catch (AssemblyException e) {
123             LOG.error("Error disassembling Program Requirement", e);
124             throw new OperationFailedException("Error disassembling Program Requirement", e);
125         }
126     }
127 
128     @Override
129     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
130 	public MajorDisciplineInfo createMajorDiscipline( String majorDisciplineTypeKey,
131                                                       MajorDisciplineInfo majorDisciplineInfo,
132                                                       ContextInfo contextInfo) throws AlreadyExistsException,
133             DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
134 
135         checkForMissingParameter(majorDisciplineInfo, "MajorDisciplineInfo");
136 
137         // Validate
138         List<ValidationResultInfo> validationResults = validateMajorDiscipline("OBJECT", majorDisciplineInfo,contextInfo);
139         if (ValidatorUtils.hasErrors(validationResults)) {
140             throw new DataValidationErrorException("Validation error!", validationResults);
141         }
142 
143         try {
144             return processMajorDisciplineInfo(majorDisciplineInfo, NodeOperation.CREATE,contextInfo);
145         } catch (AssemblyException e) {
146             LOG.error("Error creating Major Discipline", e);
147             throw new OperationFailedException("Error creating Major Discipline");
148         }
149     }
150     
151     @Override
152 	@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
153 	public MajorDisciplineInfo createNewMajorDisciplineVersion(String majorDisciplineId,  String versionComment,
154                                                                ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
155             PermissionDeniedException, VersionMismatchException, DataValidationErrorException,ReadOnlyException {
156 		//step one, get the original
157 		VersionDisplayInfo currentVersion = cluService.getCurrentVersion(CluServiceConstants.CLU_NAMESPACE_URI, majorDisciplineId,contextInfo);
158 		MajorDisciplineInfo originalMajorDiscipline = getMajorDiscipline(currentVersion.getId(),contextInfo);
159 
160 		//Version the Clu
161 		CluInfo newVersionClu = cluService.createNewCluVersion(majorDisciplineId, versionComment,contextInfo);
162 
163 		try {
164 	        BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> results;
165 
166 	        //Integrate changes into the original. (should this just be just the id?)
167 			majorDisciplineAssembler.assemble(newVersionClu, originalMajorDiscipline, true,contextInfo);
168 
169 			//Clear Ids from the original so it will make a copy and do other processing
170 			processCopy(originalMajorDiscipline, currentVersion.getId(),contextInfo);
171            
172             // Since we are creating a new version, update the requirements and statement
173 			// tree and set the state to Draft
174             List<String> programRequirementIds = originalMajorDiscipline.getProgramRequirements();
175             updateRequirementsState(programRequirementIds, DtoConstants.STATE_DRAFT,contextInfo);
176             
177 			//Disassemble the new major discipline
178 			results =  majorDisciplineAssembler.disassemble(originalMajorDiscipline, NodeOperation.UPDATE,contextInfo);
179 			
180 			// Use the results to make the appropriate service calls here
181 			programServiceMethodInvoker.invokeServiceCalls(results, contextInfo);
182 
183 			return results.getBusinessDTORef();
184 		} catch(AssemblyException e) {
185 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
186 		} catch (AlreadyExistsException e) {
187 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
188 		} catch (DependentObjectsExistException e) {
189 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
190 		} catch (CircularRelationshipException e) {
191 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
192 		} catch (UnsupportedActionException e) {
193 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
194 		} catch (CircularReferenceException e) {
195 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
196 		}
197 	}
198     
199     /**
200      * This method will update the requirement state.
201      * <p>
202      * Note that it uses StatementUtil to update the statement tree.
203      * 
204      * @param programRequirementIds
205      * @param newState
206      * @throws Exception
207      */
208     private void updateRequirementsState(List<String> programRequirementIds, String newState,ContextInfo contextInfo) throws DoesNotExistException,
209         InvalidParameterException, MissingParameterException,
210         OperationFailedException, PermissionDeniedException,  VersionMismatchException, DataValidationErrorException  {
211 
212         /*
213          * WARNING: This is an exact copy of the method from ProgramStateChangeServiceImpl.
214          * We had to copy it because we cannot reference classes in the 
215          * org.kuali.student.lum.program.server
216          * 
217          * TODO: find a place to put a shared StatementUtil 
218          */
219         if (programRequirementIds != null) { 
220             for (String programRequirementId : programRequirementIds) {
221     
222                 // Get program requirement from the program service
223                 ProgramRequirementInfo programRequirementInfo = getProgramRequirement(programRequirementId, contextInfo);
224     
225                 // Look in the requirement for the statement tree
226                 StatementTreeViewInfo statementTree = R1R2ConverterUtil.convert(programRequirementInfo.getStatement(), new StatementTreeViewInfo()) ;
227     
228                 // And recursively update the entire tree with the new state
229                 updateStatementTreeViewInfoState(newState, statementTree);
230     
231                 // Update the state of the requirement object
232                 programRequirementInfo.setStateKey(newState);
233     
234                 // The write the requirement back to the program service
235                 updateProgramRequirement(programRequirementInfo.getId(), programRequirementInfo.getTypeKey(), programRequirementInfo, contextInfo);
236     
237             }
238         }
239     }
240     
241     /**
242      * This method will recursively set the state of all statements in the tree.
243      * <p>
244      * WARNING: you must call the statement service in order to update statements.
245      * <p>
246      * 
247      * @param state is the state we should set all statements in the tree to
248      * @param statementTreeViewInfo the tree of statements
249      * @throws Exception
250      */
251     private static void updateStatementTreeViewInfoState(String state, StatementTreeViewInfo statementTreeViewInfo) {
252        /*
253         * WARNING: This is a copy of the method from StatementUtil.  We had to copy it because 
254         * we cannot reference the common.server package from this class.
255         * 
256         * TODO: find a place to put a shared StatementUtil 
257         */
258         
259         // Set the state on the statement tree itself
260         statementTreeViewInfo.setState(state);
261          
262         // Get all the requirements components for this statement
263         List<ReqComponentInfo> reqComponents = statementTreeViewInfo.getReqComponents();
264         
265         // Loop over requirements and set the state for each requirement
266         for(Iterator<ReqComponentInfo> it = reqComponents.iterator(); it.hasNext();)
267             it.next().setState(state);
268         
269         // Loop over each statement and set the state for each statement (recursively calling this method)
270         for(Iterator<StatementTreeViewInfo> itr = statementTreeViewInfo.getStatements().iterator(); itr.hasNext();)
271             updateStatementTreeViewInfoState(state, (StatementTreeViewInfo)itr.next());
272     }
273     
274 	/**
275 	 * Recurses through the statement tree and clears out ids so the tree can be copied.
276 	 * Also creates copies of clusets since they are single use
277 	 * 
278 	 * @param statementTreeView
279 	 * @throws OperationFailedException
280 	 * @see CourseServiceUtils (This is duplicate code because of the weird dependencies cause by program being in its own module)
281 	 */
282 	private void clearStatementTreeViewIdsRecursively(StatementTreeViewInfo statementTreeView,ContextInfo contextInfo) throws OperationFailedException{
283 		if(statementTreeView!=null){
284 			statementTreeView.setId(null);
285 			for(ReqComponentInfo reqComp:statementTreeView.getReqComponents()){
286 				reqComp.setId(null);
287 				for(ReqCompFieldInfo field:reqComp.getReqCompFields()){
288 					field.setId(null);
289 					//copy any clusets that are adhoc'd and set the field value to the new cluset
290 					if(ReqComponentFieldTypes.COURSE_CLUSET_KEY.getId().equals(field.getType())||
291 					   ReqComponentFieldTypes.PROGRAM_CLUSET_KEY.getId().equals(field.getType())||
292 					   ReqComponentFieldTypes.CLUSET_KEY.getId().equals(field.getType())){
293 						try {
294 							CluSetInfo cluSet = cluService.getCluSet(field.getValue(),contextInfo);
295 							cluSet.setId(null);
296 							//Clear clu ids if membership info exists, they will be re-added based on membership info 
297 							if (cluSet.getMembershipQuery() != null){
298 								cluSet.getCluIds().clear();
299 								cluSet.getCluSetIds().clear();
300 							}
301 							cluSet = cluService.createCluSet(cluSet.getTypeKey(), cluSet,contextInfo);
302 							field.setValue(cluSet.getId());
303 						} catch (Exception e) {
304 							throw new OperationFailedException("Error copying clusets.", e);
305 						}
306 					}
307 					
308 				}
309 			}
310 			//Recurse through the children
311 			for(StatementTreeViewInfo child: statementTreeView.getStatements()){
312 				clearStatementTreeViewIdsRecursively(child,contextInfo);
313 			}
314 		}
315 	}
316 
317 	/**
318      * Clears out any ids so that a subsequent call to create will copy complex structures. 
319      * Also updates VersionInfo on variations to match VersionInfo on parent.
320      * 
321      * @param majorDiscipline
322 	 * @throws PermissionDeniedException 
323 	 * @throws OperationFailedException 
324 	 * @throws MissingParameterException 
325 	 * @throws InvalidParameterException 
326 	 * @throws DoesNotExistException 
327 	 * @throws DataValidationErrorException 
328 	 * @throws AlreadyExistsException 
329 	 * @throws VersionMismatchException 
330 	 * @throws CircularRelationshipException 
331      */
332     private void processCopy(MajorDisciplineInfo majorDiscipline,String originalId,ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException, AlreadyExistsException, DataValidationErrorException, VersionMismatchException, CircularRelationshipException,ReadOnlyException {
333 		//Clear Terms (needs to be set on new version anyway so this forces the issue)
334     	majorDiscipline.setStartTerm(null);
335     	majorDiscipline.setEndTerm(null);
336     	majorDiscipline.setEndProgramEntryTerm(null);
337     	majorDiscipline.getAttributes().remove("endInstAdmitTerm");
338     	
339     	//Clear Los
340 		for(LoDisplayInfo lo:majorDiscipline.getLearningObjectives()){
341 			resetLoRecursively(lo);
342 		}
343 		//Clear OrgCoreProgram
344 		if(majorDiscipline.getOrgCoreProgram()!=null){
345 			majorDiscipline.getOrgCoreProgram().setId(null);
346 		
347 			if(majorDiscipline.getOrgCoreProgram().getLearningObjectives()!=null){
348 				for(LoDisplay lo:majorDiscipline.getOrgCoreProgram().getLearningObjectives()){
349 					resetLoRecursively((LoDisplayInfo)lo);
350 				}
351 			}
352 		}
353 		//Clear Variations
354 		for(ProgramVariationInfo variation:majorDiscipline.getVariations()){
355 			//Clear Terms (needs to be set on new version anyway so this forces the issue)
356 	    	variation.setStartTerm(null);
357 	    	variation.setEndTerm(null);
358 	    	variation.setEndProgramEntryTerm(null);
359 	    	variation.getAttributes().remove("endInstAdmitTerm");
360 	    	
361 			//Create new variation version
362 		   	String variationVersionIndId = variation.getVersion().getVersionIndId();
363 			CluInfo newVariationClu = cluService.createNewCluVersion(variationVersionIndId, "Variation version for MajorDiscipline version " + majorDiscipline.getVersion().getSequenceNumber(),contextInfo);
364 			
365 			//Create relation b/w new major discipline and new variation
366 			CluCluRelationInfo relation = new CluCluRelationInfo();
367 	        relation.setCluId(majorDiscipline.getId());
368 	        relation.setRelatedCluId(newVariationClu.getId());
369 	        relation.setTypeKey(ProgramAssemblerConstants.HAS_PROGRAM_VARIATION);
370 	        
371 	        // Relations can only be ACTIVE or Suspended
372 	        // We will set to ACTIVE for now
373 	        relation.setStateKey(DtoConstants.STATE_ACTIVE);
374 			cluService.createCluCluRelation(relation.getCluId(), relation.getRelatedCluId(), relation.getTypeKey(), relation,contextInfo);
375 	        
376 			//Set variation id & versionInfo to new variation clu
377 			variation.setId(newVariationClu.getId());
378 			variation.setMeta(newVariationClu.getMeta());
379 						
380 			//Set state to parent program's state
381 			variation.setStateKey(majorDiscipline.getStateKey());
382 			//Clear Los
383 			for(LoDisplay lo:variation.getLearningObjectives()){
384 				resetLoRecursively((LoDisplayInfo)lo);
385 			}
386 			//Copy Requirements for variation
387 			copyProgramRequirements(variation.getProgramRequirements(),majorDiscipline.getStateKey(),contextInfo);
388 		}
389 		
390 		//Copy requirements for majorDiscipline
391 		copyProgramRequirements(majorDiscipline.getProgramRequirements(),majorDiscipline.getStateKey(),contextInfo);
392 
393 		//Copy documents(create new relations to the new version)
394 		List<RefDocRelationInfo> docRelations = documentService.getRefDocRelationsByRef("kuali.org.RefObjectType.ProposalInfo", originalId, contextInfo);
395 		if(docRelations!=null){
396 			for(RefDocRelationInfo docRelation:docRelations){
397 				docRelation.setId(null);
398 				docRelation.setRefObjectId(majorDiscipline.getId());
399 				documentService.createRefDocRelation("kuali.org.RefObjectType.ProposalInfo", 
400                                         majorDiscipline.getId(), 
401                                         docRelation.getDocumentId(), 
402                                         docRelation.getTypeKey(), 
403                                         docRelation, contextInfo);
404 			}
405 		}
406 	}
407 
408 	private void processCopy(CredentialProgramInfo originaCredentialProgram,
409 			String originalId,ContextInfo contextInfo) throws OperationFailedException, AlreadyExistsException, DataValidationErrorException, InvalidParameterException, MissingParameterException, PermissionDeniedException, DoesNotExistException,ReadOnlyException {
410 		//Clear Terms (needs to be set on new version anyway so this forces the issue)
411 		originaCredentialProgram.setStartTerm(null);
412 		originaCredentialProgram.setEndTerm(null);
413 		originaCredentialProgram.setEndProgramEntryTerm(null);
414 		
415 		//Clear Los
416 		if (originaCredentialProgram.getLearningObjectives() != null){
417 			for(LoDisplay lo:originaCredentialProgram.getLearningObjectives()){
418 				resetLoRecursively((LoDisplayInfo)lo);
419 			}
420 		}
421 
422 		//Copy requirements for majorDiscipline
423 		copyProgramRequirements(originaCredentialProgram.getProgramRequirements(),originaCredentialProgram.getStateKey(),contextInfo);
424 
425 		//Copy documents(create new relations to the new version)
426 		List<RefDocRelationInfo> docRelations = documentService.getRefDocRelationsByRef("kuali.org.RefObjectType.ProposalInfo", originalId, contextInfo);
427 		if(docRelations!=null){
428 			for(RefDocRelationInfo docRelation:docRelations){
429 				docRelation.setId(null);
430 				docRelation.setRefObjectId(originaCredentialProgram.getId());
431 				documentService.createRefDocRelation("kuali.org.RefObjectType.ProposalInfo", 
432                                         originaCredentialProgram.getId(), 
433                                         docRelation.getDocumentId(), 
434                                         docRelation.getTypeKey(), 
435                                         docRelation,
436                                         contextInfo);
437 			}
438 		}
439 	}
440     
441     private void processCopy(CoreProgramInfo originalCoreProgram, String originalId,ContextInfo contextInfo) throws OperationFailedException, AlreadyExistsException, DataValidationErrorException, InvalidParameterException, MissingParameterException, PermissionDeniedException, DoesNotExistException, ReadOnlyException {
442 		//Clear Terms (needs to be set on new version anyway so this forces the issue)
443     	originalCoreProgram.setStartTerm(null);
444     	originalCoreProgram.setEndTerm(null);
445     	originalCoreProgram.setEndProgramEntryTerm(null);
446 		
447     	//Clear Los
448 		for(LoDisplay lo:originalCoreProgram.getLearningObjectives()){
449 			resetLoRecursively((LoDisplayInfo)lo);
450 		}
451 		//Copy requirements for majorDiscipline
452 		copyProgramRequirements(originalCoreProgram.getProgramRequirements(),originalCoreProgram.getStateKey(),contextInfo);
453 
454 		//Copy documents(create new relations to the new version)
455 		List<RefDocRelationInfo> docRelations = documentService.getRefDocRelationsByRef("kuali.org.RefObjectType.ProposalInfo", originalId, contextInfo);
456 		if(docRelations!=null){
457 			for(RefDocRelationInfo docRelation:docRelations){
458 				docRelation.setId(null);
459 				docRelation.setRefObjectId(originalCoreProgram.getId());
460 				documentService.createRefDocRelation("kuali.org.RefObjectType.ProposalInfo", 
461                                         originalCoreProgram.getId(), 
462                                         docRelation.getDocumentId(), 
463                                         docRelation.getTypeKey(), 
464                                         docRelation,
465                                         contextInfo);
466 			}
467 		}
468 	}
469     
470     /**
471      * Copy requirements (these exist external to the program save process and are referenced by id)
472      * @param originalProgramRequirementIds
473      * @param state
474      * @throws OperationFailedException
475      * @throws AlreadyExistsException
476      * @throws DataValidationErrorException
477      * @throws InvalidParameterException
478      * @throws MissingParameterException
479      * @throws PermissionDeniedException
480      * @throws DoesNotExistException
481      */
482     private void copyProgramRequirements(List<String> originalProgramRequirementIds,String state,ContextInfo contextInfo) throws OperationFailedException, AlreadyExistsException, DataValidationErrorException, InvalidParameterException, MissingParameterException, PermissionDeniedException, DoesNotExistException{
483 		if (originalProgramRequirementIds == null) {
484 		    return;
485 		}
486         
487         //Pull out the current requirement ids to be replaced by the ids of the new copies 
488 		List<String> programRequirementIds = new ArrayList<String>(originalProgramRequirementIds);
489 		originalProgramRequirementIds.clear();
490 		
491 		for(String programRequirementId:programRequirementIds){
492 			//Grab the original 
493 			ProgramRequirementInfo programRequirementInfo = getProgramRequirement(programRequirementId, contextInfo);
494 			//Clear the id
495 			programRequirementInfo.setId(null);
496 			
497 			programRequirementInfo.setStateKey(state);
498 			//Clear statement tree ids
499 			clearStatementTreeViewIdsRecursively(R1R2ConverterUtil.convert(programRequirementInfo.getStatement(), new StatementTreeViewInfo()),contextInfo);
500 			//Clear learning objectives
501 			for(LoDisplayInfo lo:programRequirementInfo.getLearningObjectives()){
502 				resetLoRecursively(lo);
503 			}
504 			//Create the new copy
505 			ProgramRequirementInfo createdProgramRequirement = createProgramRequirement(programRequirementInfo.getTypeKey(),programRequirementInfo,contextInfo);
506 			//add the copy's id back to the majorDiscipline's list of requirements
507 			originalProgramRequirementIds.add(createdProgramRequirement.getId());
508 		}
509     }
510     
511 	/**
512 	 * Recursively clears out the ids in a Lo and in its child Los
513 	 * @param lo
514 	 */
515 	private void resetLoRecursively(LoDisplayInfo lo){
516 		lo.getLoInfo().setId(null);
517 		for(LoDisplayInfo nestedLo:lo.getLoDisplayInfoList()){
518 			resetLoRecursively(nestedLo);
519 		}
520 	}
521 
522 	@Override
523 	@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
524 	public StatusInfo setCurrentMajorDisciplineVersion(
525 			String majorDisciplineId, Date currentVersionStart,ContextInfo contextInfo)
526 			throws DoesNotExistException, InvalidParameterException,
527 			MissingParameterException, IllegalVersionSequencingException,
528 			OperationFailedException, PermissionDeniedException, DataValidationErrorException {
529 		StatusInfo status = cluService.setCurrentCluVersion(majorDisciplineId, currentVersionStart,contextInfo);
530 		
531 		//Update the variations to be current as well
532 		List<ProgramVariationInfo> variationList = getVariationsByMajorDisciplineId(majorDisciplineId,contextInfo);
533 		for (ProgramVariationInfo variationInfo:variationList){
534 			String variationId = variationInfo.getId();
535 			//If null set to current (non-null value means version is first and is already current)
536 			
537 			if (variationInfo.getVersion().getCurrentVersionStart() == null){
538 				cluService.setCurrentCluVersion(variationId, currentVersionStart,contextInfo);
539 			}
540 		}
541 		
542 		return status;
543 	}
544 
545 	@Override
546     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
547 	public MinorDisciplineInfo createMinorDiscipline( String minorDisciplineTypeKey,
548                                                       MinorDisciplineInfo minorDisciplineInfo,  ContextInfo contextInfo) throws AlreadyExistsException,
549             DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
550         throw new UnsupportedOperationException("createMinorDiscipline");
551         //return null;
552     }
553 
554     @Override
555     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
556 	public StatusInfo deleteCredentialProgram(String credentialProgramId,  ContextInfo contextInfo)
557             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
558 
559 //        try {
560 //        	CredentialProgramInfo credentialProgram = getCredentialProgram(credentialProgramId);
561 //
562 //            processCredentialProgramInfo(credentialProgram, NodeOperation.DELETE);
563 //
564 //            return getStatus();
565 //
566 //        } catch (AssemblyException e) {
567 //            LOG.error("Error disassembling CredentialProgram", e);
568 //            throw new OperationFailedException("Error disassembling CredentialProgram");
569 //        }
570     	throw new OperationFailedException("Deletion of CredentialProgram is not supported."); 
571     }
572 
573     @Override
574     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
575 	public StatusInfo deleteHonorsProgram( String honorsProgramId, ContextInfo contextInfo) throws DoesNotExistException,
576             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
577         throw new UnsupportedOperationException("deleteHonorsProgram");
578         //return null;
579     }
580 
581     @Override
582     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
583 	public StatusInfo deleteMajorDiscipline(String majorDisciplineId,ContextInfo contextInfo)
584             throws DoesNotExistException, InvalidParameterException,
585             MissingParameterException, OperationFailedException,
586             PermissionDeniedException {
587 
588         try {
589             MajorDisciplineInfo majorDiscipline = getMajorDiscipline(majorDisciplineId,contextInfo);
590 
591             processMajorDisciplineInfo(majorDiscipline, NodeOperation.DELETE,contextInfo);
592 
593             return getStatus();
594 
595         } catch (AssemblyException e) {
596             LOG.error("Error disassembling MajorDiscipline", e);
597             throw new OperationFailedException("Error disassembling MajorDiscipline");
598         }
599     }
600 
601     @Override
602     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
603 	public StatusInfo deleteMinorDiscipline(String minorDisciplineId,ContextInfo contextInfo)
604             throws DoesNotExistException, InvalidParameterException,
605             MissingParameterException, OperationFailedException,
606             PermissionDeniedException {
607         throw new UnsupportedOperationException("deleteMinorDiscipline");
608         //return null;
609     }
610 
611     @Override
612     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
613 	public StatusInfo deleteProgramRequirement(String programRequirementId,ContextInfo contextInfo)
614             throws DoesNotExistException, InvalidParameterException,
615             MissingParameterException, OperationFailedException,
616             PermissionDeniedException {
617     	checkForMissingParameter(programRequirementId, "programRequirementId");
618         try {
619         	ProgramRequirementInfo programRequirement = getProgramRequirement(programRequirementId, contextInfo);
620 
621         	processProgramRequirement(programRequirement, NodeOperation.DELETE, contextInfo);
622 
623             return getStatus();
624 
625         } catch (AssemblyException e) {
626             LOG.error("Error disassembling MajorDiscipline", e);
627             throw new OperationFailedException("Error disassembling ProgramRequirement", e);
628         }
629 
630     }
631 
632     @Override
633     @Transactional(readOnly=true)
634     public CredentialProgramInfo getCredentialProgram(String credentialProgramId,ContextInfo contextInfo)
635             throws DoesNotExistException, InvalidParameterException,
636             MissingParameterException, OperationFailedException,
637             PermissionDeniedException {
638 
639     	CredentialProgramInfo credentialProgramInfo = null;
640 
641         try {
642             CluInfo clu = cluService.getClu(credentialProgramId,contextInfo);
643 
644             if ( ! ProgramAssemblerConstants.CREDENTIAL_PROGRAM_TYPES.contains(clu.getTypeKey()) ) {
645                 throw new DoesNotExistException("Specified CLU is not a Credential Program");
646             }
647 
648             credentialProgramInfo = credentialProgramAssembler.assemble(clu, null, false,contextInfo);
649         } catch (AssemblyException e) {
650             LOG.error("Error assembling CredentialProgram", e);
651             throw new OperationFailedException("Error assembling CredentialProgram");
652         }
653         return credentialProgramInfo;
654 
655 		// comment out the above, and uncomment below to get auto-generated data
656         // (and vice-versa)
657 //		try {
658 //			return new CredentialProgramDataGenerator(ProgramAssemblerConstants.BACCALAUREATE_PROGRAM).getCPTestData();
659 //		} catch (Exception e) {
660 //			return null;
661 //		}
662     }
663 
664 //    @Override
665 //    public LuTypeInfo getCredentialProgramType(String credentialProgramTypeKey, ContextInfo contextInfo)
666 //            throws DoesNotExistException, InvalidParameterException,
667 //            MissingParameterException, OperationFailedException {
668 //        // TODO Auto-generated method stub
669 //        return null;
670 //    }
671 
672 //    @Override
673 //    public List<LuTypeInfo> getCredentialProgramTypes(ContextInfo contextInfo)
674 //            throws OperationFailedException {
675 //        // TODO Auto-generated method stub
676 //        return null;
677 //    }
678 
679 //    @Override
680 //    public List<String> getHonorsByCredentialProgramType(String programType, ContextInfo contextInfo)
681 //            throws DoesNotExistException, InvalidParameterException,
682 //            MissingParameterException, OperationFailedException {
683 //        // TODO Auto-generated method stub
684 //        return null;
685 //    }
686 
687     @Override
688     public HonorsProgramInfo getHonorsProgram(String honorsProgramId, ContextInfo contextInfo) throws DoesNotExistException,
689             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
690         throw new UnsupportedOperationException("getHonorsProgram");
691         //return null;
692     }
693 
694     @Override
695     @Transactional(readOnly=true)
696     public MajorDisciplineInfo getMajorDiscipline(String majorDisciplineId,ContextInfo contextInfo)
697             throws DoesNotExistException, InvalidParameterException,
698             MissingParameterException, OperationFailedException,
699             PermissionDeniedException {
700 
701 
702         MajorDisciplineInfo majorDiscipline = null;
703 
704         try {
705             CluInfo clu = cluService.getClu(majorDisciplineId,contextInfo);
706             if ( ! ProgramAssemblerConstants.MAJOR_DISCIPLINE.equals(clu.getTypeKey()) ) {
707                 throw new DoesNotExistException("Specified CLU is not a Major Discipline");
708             }
709             majorDiscipline = majorDisciplineAssembler.assemble(clu, null, false,contextInfo);
710         } catch (AssemblyException e) {
711             LOG.error("Error assembling MajorDiscipline", e);
712             throw new OperationFailedException("Error assembling MajorDiscipline");
713         }
714         return majorDiscipline;
715 		// comment out the above, and uncomment below to get auto-generated data
716         // (and vice-versa)
717 //		try {
718 //			return new MajorDisciplineDataGenerator().getMajorDisciplineInfoTestData();
719 //		} catch (Exception e) {
720 //			return null;
721 //		}
722 	}
723 
724 //	@Override
725 //	public List<String> getMajorIdsByCredentialProgramType(String programType, ContextInfo contextInfo)
726 //			throws DoesNotExistException, InvalidParameterException,
727 //			MissingParameterException, OperationFailedException {
728 //		// TODO Auto-generated method stub
729 //		return null;
730 //	}
731 
732 	@Override
733 	public MinorDisciplineInfo getMinorDiscipline(String minorDisciplineId,ContextInfo contextInfo)
734 			throws DoesNotExistException, InvalidParameterException,
735 			MissingParameterException, OperationFailedException,
736 			PermissionDeniedException {
737 	    throw new UnsupportedOperationException("getMinorDiscipline");
738 		//return null;
739 	}
740 
741 	@Override
742 	public List<String> getMinorsByCredentialProgramType(String programType,ContextInfo contextInfo)
743 			throws DoesNotExistException, InvalidParameterException,
744 			MissingParameterException, OperationFailedException {
745 	    throw new UnsupportedOperationException("getMinorsByCredentialProgramType");
746 		//return null;
747 	}
748 
749 	@Override
750     @Transactional(readOnly=true)
751 	public ProgramRequirementInfo getProgramRequirement(String programRequirementId, ContextInfo contextInfo) throws DoesNotExistException,
752 			InvalidParameterException, MissingParameterException,
753 			OperationFailedException, PermissionDeniedException
754 
755     {
756 
757 		checkForMissingParameter(programRequirementId, "programRequirementId");
758 
759 		CluInfo clu = cluService.getClu(programRequirementId,contextInfo);
760 		if (!ProgramAssemblerConstants.PROGRAM_REQUIREMENT.equals(clu.getTypeKey())) {
761 			throw new DoesNotExistException("Specified CLU is not a Program Requirement");
762 		}
763 		try {
764 			ProgramRequirementInfo progReqInfo = programRequirementAssembler.assemble(clu, null, false, contextInfo);
765 			return progReqInfo;
766 		} catch (AssemblyException e) {
767             LOG.error("Error assembling program requirement", e);
768             throw new OperationFailedException("Error assembling program requirement: " + e.getMessage(), e);
769 		}
770 	}
771 
772 	@Override
773     @Transactional(readOnly=true)
774 	public List<ProgramVariationInfo> getVariationsByMajorDisciplineId(
775             String majorDisciplineId, ContextInfo contextInfo) throws DoesNotExistException,
776 			InvalidParameterException, MissingParameterException,
777 			OperationFailedException {
778     	List<ProgramVariationInfo> pvInfos = new ArrayList<ProgramVariationInfo>();
779 
780     	try {
781     			List<CluInfo> clus = cluService.getRelatedClusByCluAndRelationType(majorDisciplineId, ProgramAssemblerConstants.HAS_PROGRAM_VARIATION,contextInfo);
782 
783 		        if(clus != null && clus.size() > 0){
784 		        	for(CluInfo clu : clus){
785 		        		ProgramVariationInfo pvInfo = majorDisciplineAssembler.getProgramVariationAssembler().assemble(clu, null, false,contextInfo);
786 		        		if(pvInfo != null){
787 		        			pvInfos.add(pvInfo);
788 		        		}
789 		        	}
790 		        }
791 		    } catch (AssemblyException e) {
792 		        LOG.error("Error assembling ProgramVariation", e);
793 		        throw new OperationFailedException("Error assembling ProgramVariation");
794 		    } catch (PermissionDeniedException e) {
795 				// TODO Auto-generated catch block
796 				e.printStackTrace();
797 			}
798 
799         return pvInfos;
800     }
801 
802     @Override
803     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
804 	public CredentialProgramInfo updateCredentialProgram(
805 	        String credentialProgramId, CredentialProgramInfo credentialProgramInfo, ContextInfo contextInfo)
806             throws DataValidationErrorException, DoesNotExistException,
807             InvalidParameterException, MissingParameterException,
808             VersionMismatchException, OperationFailedException,
809             PermissionDeniedException {
810 
811         checkForMissingParameter(credentialProgramInfo, "CredentialProgramInfo");
812 
813         // Validate
814         List<ValidationResultInfo> validationResults = validateCredentialProgram("OBJECT", credentialProgramInfo,contextInfo);
815         if (ValidatorUtils.hasErrors(validationResults)) {
816             throw new DataValidationErrorException("Validation error!", validationResults);
817         }
818 
819         try {
820 
821             return processCredentialProgramInfo(credentialProgramInfo, NodeOperation.UPDATE,contextInfo);
822 
823         } catch (AssemblyException e) {
824             LOG.error("Error disassembling Credential Program", e);
825             throw new OperationFailedException("Error disassembling Credential Program");
826         }
827     }
828 
829 //    @Override
830 //    @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
831 //	public HonorsProgramInfo updateHonorsProgram(
832 //            HonorsProgramInfo honorsProgramInfo, ContextInfo contextInfo)
833 //            throws DataValidationErrorException, DoesNotExistException,
834 //            InvalidParameterException, MissingParameterException,
835 //            VersionMismatchException, OperationFailedException,
836 //            PermissionDeniedException {
837 //        // TODO Auto-generated method stub
838 //        return null;
839 //    }
840 
841     @Override
842     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
843 	public MajorDisciplineInfo updateMajorDiscipline(String majorDisciplineId,
844             MajorDisciplineInfo majorDisciplineInfo, ContextInfo contextInfo)
845             throws DataValidationErrorException, DoesNotExistException,
846             InvalidParameterException, MissingParameterException,
847             VersionMismatchException, OperationFailedException,
848             PermissionDeniedException {
849 
850         checkForMissingParameter(majorDisciplineInfo, "MajorDisciplineInfo");
851 
852         // Validate
853         List<ValidationResultInfo> validationResults = validateMajorDiscipline("OBJECT", majorDisciplineInfo,contextInfo);
854         if (ValidatorUtils.hasErrors(validationResults)) {
855             throw new DataValidationErrorException("Validation error!", validationResults);
856         }
857 
858         try {
859 
860             return processMajorDisciplineInfo(majorDisciplineInfo, NodeOperation.UPDATE,contextInfo);
861 
862         } catch (AssemblyException e) {
863             LOG.error("Error disassembling majorDiscipline", e);
864             throw new OperationFailedException("Error disassembling majorDiscipline");
865         }
866     }
867 
868     @Override
869     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
870 	public MinorDisciplineInfo updateMinorDiscipline(String minorDisciplineId, String minorDisciplineTypeKey, MinorDisciplineInfo minorDisciplineInfo, ContextInfo contextInfo)
871             throws DataValidationErrorException, DoesNotExistException,
872             InvalidParameterException, MissingParameterException,
873             VersionMismatchException, OperationFailedException,
874             PermissionDeniedException {
875         throw new UnsupportedOperationException("updateMinorDiscipline");
876         //return null;
877     }
878 
879     @Override
880     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
881 	public ProgramRequirementInfo updateProgramRequirement(String programRequirementId, String programRequirementTypeKey, 
882             ProgramRequirementInfo programRequirementInfo, ContextInfo contextInfo)
883             throws DataValidationErrorException, DoesNotExistException,
884             InvalidParameterException, MissingParameterException,
885             VersionMismatchException, OperationFailedException,
886             PermissionDeniedException {
887     	checkForMissingParameter(programRequirementInfo, "programRequirementInfo");
888         // Validate
889         List<ValidationResultInfo> validationResults = validateProgramRequirement("OBJECT", programRequirementInfo,contextInfo);
890         if (ValidatorUtils.hasErrors(validationResults)) {
891         	throw new DataValidationErrorException("Validation error!", validationResults);
892         }
893 
894         try {
895 			return processProgramRequirement(programRequirementInfo, NodeOperation.UPDATE,contextInfo);
896 		} catch (AssemblyException e) {
897 			throw new OperationFailedException("Unable to update ProgramRequirement", e);
898 		}
899     }
900 
901     @Override
902     public List<ValidationResultInfo> validateCredentialProgram(
903             String validationType, CredentialProgramInfo credentialProgramInfo,ContextInfo contextInfo)
904             throws InvalidParameterException,
905             MissingParameterException, OperationFailedException {
906 
907         List<ValidationResultInfo> validationResults = new ArrayList<ValidationResultInfo>();
908 //        if ( ! ProgramAssemblerConstants.DRAFT.equals(credentialProgramInfo.getState()) ) {
909             ObjectStructureDefinition objStructure = this.getObjectStructure(CredentialProgramInfo.class.getName());
910             Validator validator = validatorFactory.getValidator();
911             validationResults.addAll(validator.validateObject(credentialProgramInfo, objStructure,contextInfo));
912 //        }
913 
914         return validationResults;
915     }
916 
917     @Override
918     public List<ValidationResultInfo> validateHonorsProgram(
919             String validationType, HonorsProgramInfo honorsProgramInfo,ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, OperationFailedException {
920         throw new UnsupportedOperationException("validateHonorsProgram");
921         //return null;
922     }
923 
924     @Override
925     public List<ValidationResultInfo> validateMajorDiscipline(
926             String validationType, MajorDisciplineInfo majorDisciplineInfo,ContextInfo contextInfo)
927             throws InvalidParameterException,
928             MissingParameterException, OperationFailedException,PermissionDeniedException {
929 
930         List<ValidationResultInfo> validationResults = new ArrayList<ValidationResultInfo>();
931 //        if ( ! ProgramAssemblerConstants.DRAFT.equalsIgnoreCase(majorDisciplineInfo.getState()) ) {
932             ObjectStructureDefinition objStructure = this.getObjectStructure(MajorDisciplineInfo.class.getName());
933             Validator validator = validatorFactory.getValidator();
934             validationResults.addAll(validator.validateObject(majorDisciplineInfo, objStructure,contextInfo));
935 //        }
936         validateMajorDisciplineAtps(majorDisciplineInfo,validationResults,contextInfo);
937         return validationResults;
938     }
939 
940     @Override
941     public List<ValidationResultInfo> validateMinorDiscipline(
942             String validationType, MinorDisciplineInfo minorDisciplineInfo,ContextInfo contextInfo   )
943             throws InvalidParameterException,
944             MissingParameterException, OperationFailedException {
945         throw new UnsupportedOperationException("validateMinorDiscipline");
946         //return null ;
947     }
948 
949     @Override
950     public List<ValidationResultInfo> validateProgramRequirement(
951             String validationType, ProgramRequirementInfo programRequirementInfo,ContextInfo contextInfo)
952             throws InvalidParameterException,
953             MissingParameterException, OperationFailedException {
954 
955         ObjectStructureDefinition objStructure = this.getObjectStructure(ProgramRequirementInfo.class.getName());
956         Validator validator = validatorFactory.getValidator();
957         List<ValidationResultInfo> validationResults = validator.validateObject(programRequirementInfo, objStructure,contextInfo);
958 
959         return validationResults;
960     }
961 
962 
963     @Override
964     public ObjectStructureDefinition getObjectStructure(String objectTypeKey) {
965         return dictionaryService.getObjectStructure(objectTypeKey);
966     }
967 
968 
969    @Override
970     public List<String> getObjectTypes() {
971         return dictionaryService.getObjectTypes();
972     }
973 
974     @Override
975     public List<TypeInfo> getSearchTypes( ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, OperationFailedException {
976         throw new UnsupportedOperationException("getSearchTypes");
977     }
978 
979     @Override
980     public TypeInfo getSearchType(String searchTypeKey,  ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException {
981         throw new UnsupportedOperationException("getSearchType");
982     }
983 
984     @Override
985     public SearchResultInfo search(SearchRequestInfo searchRequestInfo,  ContextInfo contextInfo) throws MissingParameterException, OperationFailedException, PermissionDeniedException {
986         throw new UnsupportedOperationException("ProgramService.search");
987     }
988 
989     /**
990      * Check for missing parameter and throw localized exception if missing
991      *
992      * @param param
993      * @param paramName
994      * @throws MissingParameterException
995      */
996     private void checkForMissingParameter(Object param, String paramName)
997             throws MissingParameterException {
998         if (param == null) {
999             throw new MissingParameterException(paramName + " can not be null");
1000         }
1001     }
1002 
1003     // TODO - when CRUD for a second ProgramInfo is implemented, pull common code up from its process*() and this
1004 
1005     private MajorDisciplineInfo processMajorDisciplineInfo(MajorDisciplineInfo majorDisciplineInfo, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException {
1006 
1007         BaseDTOAssemblyNode<MajorDisciplineInfo, CluInfo> results = majorDisciplineAssembler.disassemble(majorDisciplineInfo, operation, contextInfo);
1008         invokeServiceCalls(results, contextInfo);
1009         return results.getBusinessDTORef();
1010     }
1011 
1012     private CredentialProgramInfo processCredentialProgramInfo(CredentialProgramInfo credentialProgramInfo, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException {
1013 
1014         BaseDTOAssemblyNode<CredentialProgramInfo, CluInfo> results = credentialProgramAssembler.disassemble(credentialProgramInfo, operation, contextInfo);
1015         invokeServiceCalls(results, contextInfo);
1016         return results.getBusinessDTORef();
1017     }
1018 
1019     private ProgramRequirementInfo processProgramRequirement(ProgramRequirementInfo programRequirementInfo, NodeOperation operation, ContextInfo contextInfo) throws AssemblyException {
1020     	BOAssembler<ProgramRequirementInfo, CluInfo> passAlong;
1021         BaseDTOAssemblyNode<ProgramRequirementInfo, CluInfo> results = programRequirementAssembler.disassemble(programRequirementInfo, operation, contextInfo);
1022         invokeServiceCalls(results, contextInfo);
1023         return results.getBusinessDTORef();
1024     }
1025 
1026 	private void invokeServiceCalls(BaseDTOAssemblyNode<?, CluInfo> results, ContextInfo contextInfo) throws AssemblyException{
1027         // Use the results to make the appropriate service calls here
1028         try {
1029             programServiceMethodInvoker.invokeServiceCalls(results, contextInfo);
1030         } catch (AssemblyException e) {
1031         	throw e;
1032         } catch (Exception e) {
1033             throw new AssemblyException(e);
1034         }
1035     }
1036 
1037     //Spring setters. Used by spring container to inject corresponding dependencies.
1038 
1039     public void setCluService(CluService cluService) {
1040         this.cluService = cluService;
1041     }
1042 
1043     public CluService getCluService() {
1044 		return cluService;
1045 	}
1046 
1047 	public void setDictionaryService(DictionaryService dictionaryService) {
1048         this.dictionaryService = dictionaryService;
1049     }
1050 
1051     public DictionaryService getDictionaryService() {
1052 		return dictionaryService;
1053 	}
1054 
1055     public void setSearchManager(SearchManager searchManager) {
1056         this.searchManager = searchManager;
1057     }
1058     
1059 	public SearchManager getSearchManager() {
1060 		return searchManager;
1061 	}
1062 
1063 	public void setMajorDisciplineAssembler(MajorDisciplineAssembler majorDisciplineAssembler) {
1064         this.majorDisciplineAssembler = majorDisciplineAssembler;
1065     }
1066 
1067 	public MajorDisciplineAssembler getMajorDisciplineAssembler() {
1068 		return majorDisciplineAssembler;
1069 	}
1070 
1071 	public void setCredentialProgramAssembler(
1072 			CredentialProgramAssembler credentialProgramAssembler) {
1073 		this.credentialProgramAssembler = credentialProgramAssembler;
1074 	}
1075 
1076 	public CredentialProgramAssembler getCredentialProgramAssembler() {
1077 		return credentialProgramAssembler;
1078 	}
1079 
1080 	public void setProgramRequirementAssembler(ProgramRequirementAssembler programRequirementAssembler) {
1081         this.programRequirementAssembler = programRequirementAssembler;
1082     }
1083 
1084     public ProgramRequirementAssembler getProgramRequirementAssembler() {
1085 		return programRequirementAssembler;
1086 	}
1087 
1088 	public void setProgramServiceMethodInvoker(BusinessServiceMethodInvoker serviceMethodInvoker) {
1089         this.programServiceMethodInvoker = serviceMethodInvoker;
1090     }
1091 
1092     public BusinessServiceMethodInvoker getProgramServiceMethodInvoker() {
1093 		return programServiceMethodInvoker;
1094 	}
1095 
1096 	public void setValidatorFactory(ValidatorFactory validatorFactory) {
1097         this.validatorFactory = validatorFactory;
1098     }
1099 
1100 	public ValidatorFactory getValidatorFactory() {
1101 		return validatorFactory;
1102 	}
1103 	
1104 	public void setCoreProgramAssembler(CoreProgramAssembler coreProgramAssembler) {
1105 		this.coreProgramAssembler = coreProgramAssembler;
1106 	}
1107 
1108 	public CoreProgramAssembler getCoreProgramAssembler() {
1109 		return coreProgramAssembler;
1110 	}
1111 
1112 	private StatusInfo getStatus(){
1113         StatusInfo status = new StatusInfo();
1114         status.setSuccess(true);
1115         return status;
1116 	}
1117 
1118     private CoreProgramInfo processCoreProgramInfo(CoreProgramInfo coreProgramInfo, NodeOperation operation,ContextInfo contextInfo) throws AssemblyException, InvalidParameterException, MissingParameterException, DoesNotExistException, OperationFailedException, PermissionDeniedException {
1119         BaseDTOAssemblyNode<CoreProgramInfo, CluInfo> results = coreProgramAssembler.disassemble(coreProgramInfo, operation, contextInfo);
1120         invokeServiceCalls(results, contextInfo);
1121         return results.getBusinessDTORef();
1122     }
1123 
1124     @Override
1125     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
1126 	public CoreProgramInfo createCoreProgram(String coreProgramTypeKey, CoreProgramInfo coreProgramInfo,ContextInfo contextInfo) throws AlreadyExistsException, DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException, DoesNotExistException {
1127         checkForMissingParameter(coreProgramInfo, "CoreProgramInfo");
1128         
1129         // Validate
1130         List<ValidationResultInfo> validationResults = validateCoreProgram("OBJECT", coreProgramInfo, contextInfo );
1131         if (ValidatorUtils.hasErrors(validationResults)) {
1132             throw new DataValidationErrorException("Validation error!", validationResults);
1133         }
1134 
1135         try {
1136             return processCoreProgramInfo(coreProgramInfo, NodeOperation.CREATE,contextInfo);
1137         } catch (AssemblyException e) {
1138             LOG.error("Error disassembling CoreProgram", e);
1139             throw new OperationFailedException("Error disassembling CoreProgram");
1140         }
1141     }
1142 
1143 	@Override
1144 	@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
1145 	public CoreProgramInfo createNewCoreProgramVersion(
1146 			String coreProgramId, String versionComment,ContextInfo contextInfo)
1147 			throws DoesNotExistException, InvalidParameterException,
1148 			MissingParameterException, OperationFailedException,
1149 			PermissionDeniedException, VersionMismatchException,
1150 			DataValidationErrorException, ReadOnlyException {
1151 		//step one, get the original
1152 		VersionDisplayInfo currentVersion = cluService.getCurrentVersion(CluServiceConstants.CLU_NAMESPACE_URI, coreProgramId,contextInfo);
1153 		CoreProgramInfo originalCoreProgram = getCoreProgram(currentVersion.getId(),contextInfo);
1154 
1155 		//Version the Clu
1156 		CluInfo newVersionClu = cluService.createNewCluVersion(coreProgramId, versionComment,contextInfo);
1157 
1158 		try {
1159 	        BaseDTOAssemblyNode<CoreProgramInfo, CluInfo> results;
1160 
1161 	        //Integrate changes into the original. (should this just be just the id?)
1162 			coreProgramAssembler.assemble(newVersionClu, originalCoreProgram, true, contextInfo);
1163 			
1164 			//Clear Ids from the original so it will make a copy and do other processing
1165 			processCopy(originalCoreProgram, currentVersion.getId(),contextInfo);
1166 
1167 			//Disassemble the new
1168 			results = coreProgramAssembler.disassemble(originalCoreProgram, NodeOperation.UPDATE, contextInfo);
1169 
1170 			// Use the results to make the appropriate service calls here
1171 			programServiceMethodInvoker.invokeServiceCalls(results, contextInfo);
1172 
1173 			return results.getBusinessDTORef();
1174 		} catch(AssemblyException e) {
1175 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1176 		} catch (AlreadyExistsException e) {
1177 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1178 		} catch (DependentObjectsExistException e) {
1179 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1180 		} catch (CircularRelationshipException e) {
1181 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1182 		} catch (UnsupportedActionException e) {
1183 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1184 		} catch (CircularReferenceException e) {
1185 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1186 		}
1187 	}
1188     
1189 
1190 
1191 	@Override
1192     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
1193 	public StatusInfo deleteCoreProgram(String coreProgramId, ContextInfo contextInfo) throws DoesNotExistException,
1194             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException{
1195 //        try {
1196 //        	CoreProgramInfo coreProgramInfo = getCoreProgram(coreProgramId);
1197 //
1198 //            processCoreProgramInfo(coreProgramInfo, NodeOperation.DELETE);
1199 //
1200 //            return getStatus();
1201 //
1202 //        } catch (AssemblyException e) {
1203 //            LOG.error("Error disassembling CoreProgram", e);
1204 //            throw new OperationFailedException("Error disassembling CoreProgram");
1205 //        }
1206     	throw new OperationFailedException("Deletion of CoreProgram is not supported."); 
1207     }
1208 
1209     @Override
1210     @Transactional(readOnly=true)
1211     public CoreProgramInfo getCoreProgram(String coreProgramId,ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
1212     	CoreProgramInfo coreProgramInfo = null;
1213 
1214         try {
1215             CluInfo clu = cluService.getClu(coreProgramId,contextInfo);
1216             if ( ! ProgramAssemblerConstants.CORE_PROGRAM.equals(clu.getTypeKey()) ) {
1217                 throw new DoesNotExistException("Specified CLU is not a CoreProgram");
1218             }
1219             coreProgramInfo = coreProgramAssembler.assemble(clu, null, false, contextInfo);
1220         } catch (AssemblyException e) {
1221             LOG.error("Error assembling CoreProgram", e);
1222             throw new OperationFailedException("Error assembling CoreProgram");
1223         }
1224         return coreProgramInfo;
1225 		// comment out the above, and uncomment below to get auto-generated data
1226         // (and vice-versa)
1227 //		try {
1228 //			return new CoreProgramDataGenerator().getCoreProgramInfoTestData();
1229 //		} catch (Exception e) {
1230 //			return null;
1231 //		}
1232     }
1233 
1234     @Override
1235     @Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
1236 	public CoreProgramInfo updateCoreProgram( String coreProgramId,  String coreProgramTypeKey,
1237                                               CoreProgramInfo coreProgramInfo,  ContextInfo contextInfo) throws DataValidationErrorException, DoesNotExistException,
1238             InvalidParameterException, MissingParameterException, VersionMismatchException, OperationFailedException, PermissionDeniedException{
1239         checkForMissingParameter(coreProgramInfo, "CoreProgramInfo");
1240         
1241         // Validate
1242         List<ValidationResultInfo> validationResults = validateCoreProgram("OBJECT", coreProgramInfo,contextInfo);
1243         if (ValidatorUtils.hasErrors(validationResults)) {
1244             throw new DataValidationErrorException("Validation error!", validationResults);
1245         }
1246 
1247         try {
1248 
1249             return processCoreProgramInfo(coreProgramInfo, NodeOperation.UPDATE,contextInfo);
1250 
1251         } catch (AssemblyException e) {
1252             LOG.error("Error disassembling CoreProgram", e);
1253             throw new OperationFailedException("Error disassembling CoreProgram");
1254         }
1255     }
1256 
1257     @Override
1258     public List<ValidationResultInfo> validateCoreProgram( String validationType,  CoreProgramInfo coreProgramInfo,
1259                                                            ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, OperationFailedException{
1260         List<ValidationResultInfo> validationResults = new ArrayList<ValidationResultInfo>();
1261 //        if ( ! ProgramAssemblerConstants.DRAFT.equals(coreProgramInfo.getState()) ) {
1262 	        ObjectStructureDefinition objStructure = this.getObjectStructure(CoreProgramInfo.class.getName());
1263 	        Validator validator = validatorFactory.getValidator();
1264             validationResults.addAll(validator.validateObject(coreProgramInfo, objStructure,contextInfo));
1265 //        }
1266         return validationResults;
1267     }
1268         
1269         
1270 	@Override
1271 	@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
1272 	public CredentialProgramInfo createNewCredentialProgramVersion( String credentialProgramId,
1273                                                                     String versionComment,
1274                                                                     ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
1275             PermissionDeniedException, VersionMismatchException, DataValidationErrorException, ReadOnlyException {
1276 		//step one, get the original
1277 		VersionDisplayInfo currentVersion = cluService.getCurrentVersion(CluServiceConstants.CLU_NAMESPACE_URI, credentialProgramId,contextInfo);
1278 		CredentialProgramInfo originaCredentialProgram = getCredentialProgram(currentVersion.getId(),contextInfo);
1279 
1280 		//Version the Clu
1281 		CluInfo newVersionClu = cluService.createNewCluVersion(credentialProgramId, versionComment,contextInfo);
1282 
1283 		try {
1284 
1285 	        BaseDTOAssemblyNode<CredentialProgramInfo, CluInfo> results;
1286 
1287 	        //Integrate changes into the original. (should this just be just the id?)
1288 	        
1289 			credentialProgramAssembler.assemble(newVersionClu, originaCredentialProgram, true, contextInfo);
1290 
1291 			//Clear Ids from the original so it will make a copy and do other processing
1292 
1293 			processCopy(originaCredentialProgram, currentVersion.getId(),contextInfo);
1294 
1295 			//Disassemble the new -- Convert the R2 to R1 before it is passed....
1296 			results = credentialProgramAssembler.disassemble(originaCredentialProgram, NodeOperation.UPDATE, contextInfo);
1297 
1298 			// Use the results to make the appropriate service calls here
1299 			programServiceMethodInvoker.invokeServiceCalls(results, contextInfo);
1300 			
1301 			//Here we get a R1 object that is then convert to and R2 object and then returned via the return statement below.
1302 			return results.getBusinessDTORef();
1303 		} catch(AssemblyException e) {
1304 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1305 		} catch (AlreadyExistsException e) {
1306 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1307 		} catch (DependentObjectsExistException e) {
1308 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1309 		} catch (CircularRelationshipException e) {
1310 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1311 		} catch (UnsupportedActionException e) {
1312 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1313 		} catch (CircularReferenceException e) {
1314 			throw new OperationFailedException("Error creating new MajorDiscipline version",e);
1315 		}
1316 	}
1317 
1318 
1319 	@Override
1320 	@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
1321 	public StatusInfo setCurrentCoreProgramVersion( String coreProgramId,  Date currentVersionStart,
1322                                                     ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, IllegalVersionSequencingException,
1323             OperationFailedException, PermissionDeniedException, DataValidationErrorException{
1324 		StatusInfo status = cluService.setCurrentCluVersion(coreProgramId, currentVersionStart,contextInfo);
1325 		
1326 		return status;
1327 	}
1328 
1329 	@Override
1330 	@Transactional(readOnly=false,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
1331 	public StatusInfo setCurrentCredentialProgramVersion( String credentialProgramId,  Date currentVersionStart,
1332                                                           ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, IllegalVersionSequencingException,
1333             OperationFailedException, PermissionDeniedException, DataValidationErrorException {
1334 		StatusInfo status = cluService.setCurrentCluVersion(credentialProgramId, currentVersionStart,contextInfo);
1335 		
1336 		return status;
1337 	}
1338 
1339 	@Override
1340     @Transactional(readOnly=true)
1341 	public VersionDisplayInfo getCurrentVersion(String refObjectTypeURI,
1342 			String refObjectId,ContextInfo contextInfo) throws DoesNotExistException,
1343 			InvalidParameterException, MissingParameterException,
1344 			OperationFailedException, PermissionDeniedException {
1345 		if(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI.equals(refObjectTypeURI)){
1346 			return cluService.getCurrentVersion(CluServiceConstants.CLU_NAMESPACE_URI, refObjectId,contextInfo);
1347 		}
1348 		throw new InvalidParameterException("Object type: " + refObjectTypeURI + " is not known to this implementation");
1349 	}
1350 
1351 	@Override
1352     @Transactional(readOnly=true)
1353 	public VersionDisplayInfo getCurrentVersionOnDate(String refObjectTypeURI,
1354                                                       String refObjectId, Date date, ContextInfo contextInfo) throws DoesNotExistException,
1355 			InvalidParameterException, MissingParameterException,
1356 			OperationFailedException, PermissionDeniedException {
1357 		if(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI.equals(refObjectTypeURI)){
1358 			return cluService.getCurrentVersionOnDate(CluServiceConstants.CLU_NAMESPACE_URI, refObjectId, date,contextInfo);
1359 		}
1360 		throw new InvalidParameterException("Object type: " + refObjectTypeURI + " is not known to this implementation");
1361 	}
1362 
1363 	@Override
1364     @Transactional(readOnly=true)
1365 	public VersionDisplayInfo getFirstVersion(String refObjectTypeURI,
1366                                               String refObjectId, ContextInfo contextInfo) throws DoesNotExistException,
1367 			InvalidParameterException, MissingParameterException,
1368 			OperationFailedException, PermissionDeniedException {
1369 		if(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI.equals(refObjectTypeURI)){
1370 			return cluService.getFirstVersion(CluServiceConstants.CLU_NAMESPACE_URI, refObjectId,contextInfo);
1371 		}
1372 		throw new InvalidParameterException("Object type: " + refObjectTypeURI + " is not known to this implementation");
1373 
1374 	}
1375 
1376 	@Override
1377     @Transactional(readOnly=true)
1378 	public VersionDisplayInfo getLatestVersion(String refObjectTypeURI,
1379                                                String refObjectId, ContextInfo contextInfo) throws DoesNotExistException,
1380 			InvalidParameterException, MissingParameterException,
1381 			OperationFailedException, PermissionDeniedException {
1382 		if(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI.equals(refObjectTypeURI)){
1383 			return cluService.getLatestVersion(CluServiceConstants.CLU_NAMESPACE_URI, refObjectId,contextInfo);
1384 		}
1385 		throw new InvalidParameterException("Object type: " + refObjectTypeURI + " is not known to this implementation");
1386 
1387 	}
1388 
1389 	@Override
1390     @Transactional(readOnly=true)
1391 	public VersionDisplayInfo getVersionBySequenceNumber(
1392             String refObjectTypeURI, String refObjectId, Long sequence, ContextInfo contextInfo)
1393 			throws DoesNotExistException, InvalidParameterException,
1394 			MissingParameterException, OperationFailedException,
1395 			PermissionDeniedException {
1396 		if(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI.equals(refObjectTypeURI)){
1397 			return cluService.getVersionBySequenceNumber(CluServiceConstants.CLU_NAMESPACE_URI, refObjectId, sequence,contextInfo);
1398 		}
1399 		throw new InvalidParameterException("Object type: " + refObjectTypeURI + " is not known to this implementation");
1400 	}
1401 
1402 	@Override
1403     @Transactional(readOnly=true)
1404 	public List<VersionDisplayInfo> getVersions(String refObjectTypeURI,
1405 			String refObjectId,ContextInfo contextInfo) throws DoesNotExistException,
1406 			InvalidParameterException, MissingParameterException,
1407 			OperationFailedException, PermissionDeniedException {
1408 		if(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI.equals(refObjectTypeURI)){
1409 			return cluService.getVersions(CluServiceConstants.CLU_NAMESPACE_URI, refObjectId,contextInfo);
1410 		}
1411 		throw new InvalidParameterException("Object type: " + refObjectTypeURI + " is not known to this implementation");
1412 	}
1413 
1414 	@Override
1415     @Transactional(readOnly=true)
1416 	public List<VersionDisplayInfo> getVersionsInDateRange(
1417             String refObjectTypeURI, String refObjectId, Date from, Date to, ContextInfo contextInfo)
1418 			throws DoesNotExistException, InvalidParameterException,
1419 			MissingParameterException, OperationFailedException,
1420 			PermissionDeniedException {
1421 		if(ProgramServiceConstants.PROGRAM_NAMESPACE_MAJOR_DISCIPLINE_URI.equals(refObjectTypeURI)){
1422 			return cluService.getVersionsInDateRange(CluServiceConstants.CLU_NAMESPACE_URI, refObjectId, from, to,contextInfo);
1423 		}
1424 		throw new InvalidParameterException("Object type: " + refObjectTypeURI + " is not known to this implementation");
1425 	}
1426 
1427 	public void setAtpService(AtpService atpService) {
1428 		this.atpService = atpService;
1429 	}
1430 
1431 	public AtpService getAtpService() {
1432 		return atpService;
1433 	}
1434 
1435 	private void validateMajorDisciplineAtps(MajorDisciplineInfo majorDisciplineInfo, List<ValidationResultInfo> validationResults,ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
1436 		String startTerm = majorDisciplineInfo.getStartTerm();
1437 
1438         String endInstAdmitTerm = majorDisciplineInfo.getAttributeValue("endInstAdmitTerm");
1439 		if(endInstAdmitTerm != null) {
1440             compareAtps(startTerm, endInstAdmitTerm, validationResults, "End Inst Admin Term", "endInstAdmitTerm",contextInfo);
1441 		}
1442 		if(!isEmpty(majorDisciplineInfo.getEndProgramEntryTerm())){
1443 			compareAtps(startTerm, majorDisciplineInfo.getEndTerm(), validationResults, "End Program Entry Term", "endProgramEntryTerm",contextInfo);
1444 		}
1445 		
1446 		if(!isEmpty(majorDisciplineInfo.getEndTerm())){
1447 			compareAtps(startTerm, majorDisciplineInfo.getEndTerm(), validationResults, "End Program Enroll Term", "endTerm",contextInfo);
1448 		}		
1449 		
1450 		List<ProgramVariationInfo> variations = majorDisciplineInfo.getVariations();
1451 		if(variations != null && !variations.isEmpty()){
1452 			int idx = 0;
1453 			for(ProgramVariationInfo variation : variations){
1454 				validateVariationAtps(variation, validationResults, idx,contextInfo);
1455 				idx ++;
1456 			}
1457 		}
1458 	}
1459 	
1460 	//FIXME, this validation should be moved into a custom validation class + configuration
1461 	private void validateVariationAtps(ProgramVariationInfo programVariationInfo, List<ValidationResultInfo> validationResults, int idx,ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, OperationFailedException,PermissionDeniedException{
1462 		
1463 		String startTerm = programVariationInfo.getStartTerm();
1464         String endInstAdmitTerm = programVariationInfo.getAttributeValue("endInstAdmitTerm");
1465 		if(endInstAdmitTerm != null) {
1466             compareAtps(startTerm, endInstAdmitTerm, validationResults, "End Inst Admin Term",  "variations/" + idx + "/endInstAdmitTerm",contextInfo);
1467 		}
1468 	
1469 		if(!isEmpty(programVariationInfo.getEndProgramEntryTerm())){
1470 			compareAtps(startTerm, programVariationInfo.getEndProgramEntryTerm(), validationResults, "End Program Entry Term", "variations/" + idx + "/endProgramEntryTerm",contextInfo);
1471 		}
1472 		
1473 		if(!isEmpty(programVariationInfo.getEndTerm())){
1474 			compareAtps(startTerm, programVariationInfo.getEndTerm(), validationResults, "End Program Enroll Term", "variations/" + idx + "/endTerm",contextInfo);
1475 		}
1476 	}
1477 	
1478 	private AtpInfo getAtpInfo(String atpKey,ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,PermissionDeniedException{
1479 		if(atpKey==null){
1480 			return null;
1481 		}
1482 		return atpService.getAtp(atpKey, contextInfo);
1483 	}
1484 	//FIXME error should return using message service and not static text
1485 	private void compareAtps(String aptKey1, String aptKey2, List<ValidationResultInfo> validationResults, String field, String path,ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException{
1486 		AtpInfo atpInfo1 = null;
1487 		AtpInfo atpInfo2 = null;
1488 		
1489 		try{
1490 			atpInfo1 = getAtpInfo(aptKey1,contextInfo);
1491 			atpInfo2 = getAtpInfo(aptKey2,contextInfo);
1492 		}catch(DoesNotExistException e){}
1493 		
1494 		if(atpInfo1 != null && atpInfo1 != null){
1495 			if(atpInfo1.getStartDate()!= null && atpInfo2.getStartDate() != null){			
1496 				boolean compareResult = ValidatorUtils.compareValues(atpInfo2.getStartDate(), atpInfo1.getStartDate(), DataType.DATE, "greater_than_equal", true, new ServerDateParser());
1497 				if(!compareResult){
1498 					ValidationResultInfo vri = new ValidationResultInfo();
1499 					vri.setElement(path);
1500 					vri.setError(field + " should be greater than Start Term");
1501 					validationResults.add(vri);
1502 				}
1503 			}
1504 		}
1505 			
1506 	}
1507 	
1508 	private boolean isEmpty(String value){
1509 		return value == null || (value != null && "".equals(value));
1510 	}
1511 
1512 	public void setDocumentService(DocumentService documentService) {
1513 		this.documentService = documentService;
1514 	}
1515 
1516 	public DocumentService getDocumentService() {
1517 		return documentService;
1518 	}
1519 
1520 	@Override
1521 	public List<CredentialProgramInfo> getCredentialProgramsByIds(
1522 			List<String> credentialProgramIds, ContextInfo contextInfo)
1523 			throws DoesNotExistException, InvalidParameterException,
1524 			MissingParameterException, OperationFailedException,
1525 			PermissionDeniedException {
1526 	    throw new UnsupportedOperationException("getCredentialProgramsByIds");
1527 		//return null;
1528 	}
1529 
1530 	@Override
1531 	public List<MajorDisciplineInfo> getMajorDisciplinesByIds(
1532 			List<String> majorDisciplineIds, ContextInfo contextInfo)
1533 			throws DoesNotExistException, InvalidParameterException,
1534 			MissingParameterException, OperationFailedException,
1535 			PermissionDeniedException {
1536 	    throw new UnsupportedOperationException("getMajorDisciplinesByIds");
1537 		//return null;
1538 	}
1539 
1540 	@Override
1541 	public List<String> getMajorDisciplineIdsByCredentialProgramType(
1542 			String programType, ContextInfo contextInfo)
1543 			throws DoesNotExistException, InvalidParameterException,
1544 			MissingParameterException, OperationFailedException {
1545 	    throw new UnsupportedOperationException("getMajorDisciplineIdsByCredentialProgramType");
1546 		//return null;
1547 	}
1548 
1549 	@Override
1550 	public List<HonorsProgramInfo> getHonorsProgramsByIds(
1551 			List<String> honorsProgramIds, ContextInfo contextInfo)
1552 			throws DoesNotExistException, InvalidParameterException,
1553 			MissingParameterException, OperationFailedException,
1554 			PermissionDeniedException {
1555 	    throw new UnsupportedOperationException("getHonorsProgramsByIds");
1556 		//return null;
1557 	}
1558 
1559 	@Override
1560 	public List<String> getHonorProgramIdsByCredentialProgramType(
1561 			String programType, ContextInfo contextInfo)
1562 			throws DoesNotExistException, InvalidParameterException,
1563 			MissingParameterException, OperationFailedException {
1564 	    throw new UnsupportedOperationException("getHonorProgramIdsByCredentialProgramType");
1565 		//return null;
1566 	}
1567 
1568 	@Override
1569 	public HonorsProgramInfo updateHonorsProgram(String honorsProgramId,
1570 			String honorsProgramTypeKey, HonorsProgramInfo honorsProgramInfo,
1571 			ContextInfo contextInfo) throws DataValidationErrorException,
1572 			DoesNotExistException, InvalidParameterException,
1573 			MissingParameterException, VersionMismatchException,
1574 			OperationFailedException, PermissionDeniedException {
1575 	    throw new UnsupportedOperationException("updateHonorsProgram");
1576 		//return null;
1577 	}
1578 
1579 	@Override
1580 	public List<CoreProgramInfo> getCoreProgramsByIds(
1581 			List<String> coreProgramIds, ContextInfo contextInfo)
1582 			throws DoesNotExistException, InvalidParameterException,
1583 			MissingParameterException, OperationFailedException,
1584 			PermissionDeniedException {
1585 	    throw new UnsupportedOperationException("getCoreProgramsByIds");
1586 		//return null;
1587 	}
1588 
1589 	@Override
1590 	public List<ProgramRequirementInfo> getProgramRequirementsByIds(
1591 			List<String> programRequirementIds, ContextInfo contextInfo)
1592 			throws DoesNotExistException, InvalidParameterException,
1593 			MissingParameterException, OperationFailedException,
1594 			PermissionDeniedException {
1595 	    throw new UnsupportedOperationException("getProgramRequirementsByIds");
1596 		//return null;
1597 	}
1598 
1599 	@Override
1600 	public List<ProgramVariationInfo> getProgramVariationsByMajorDiscipline(
1601 			String majorDisciplineId, ContextInfo contextInfo)
1602 			throws DoesNotExistException, InvalidParameterException,
1603 			MissingParameterException, OperationFailedException {
1604 	    throw new UnsupportedOperationException("getProgramVariationsByMajorDiscipline");
1605 		//return null;
1606 	}
1607 
1608 }