View Javadoc

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