001/** 002 * Copyright 2005-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krms.service.impl; 017 018import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 019import org.kuali.rice.core.api.util.tree.Node; 020import org.kuali.rice.core.api.util.tree.Tree; 021import org.kuali.rice.krad.bo.Note; 022import org.kuali.rice.krad.maintenance.MaintenanceDocument; 023import org.kuali.rice.krad.uif.container.CollectionGroup; 024import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 025import org.kuali.rice.krad.uif.view.View; 026import org.kuali.rice.krad.util.KRADConstants; 027import org.kuali.rice.krad.web.form.MaintenanceDocumentForm; 028import org.kuali.rice.krms.api.KrmsConstants; 029import org.kuali.rice.krms.api.repository.RuleManagementService; 030import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition; 031import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition; 032import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition; 033import org.kuali.rice.krms.api.repository.agenda.AgendaTreeEntryDefinitionContract; 034import org.kuali.rice.krms.api.repository.agenda.AgendaTreeRuleEntry; 035import org.kuali.rice.krms.api.repository.proposition.PropositionType; 036import org.kuali.rice.krms.api.repository.rule.RuleDefinition; 037import org.kuali.rice.krms.api.repository.term.TermDefinition; 038import org.kuali.rice.krms.api.repository.term.TermRepositoryService; 039import org.kuali.rice.krms.api.repository.term.TermResolverDefinition; 040import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition; 041import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition; 042import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService; 043import org.kuali.rice.krms.dto.AgendaEditor; 044import org.kuali.rice.krms.dto.PropositionEditor; 045import org.kuali.rice.krms.dto.PropositionParameterEditor; 046import org.kuali.rice.krms.dto.RuleEditor; 047import org.kuali.rice.krms.dto.RuleManagementWrapper; 048import org.kuali.rice.krms.dto.TemplateInfo; 049import org.kuali.rice.krms.dto.TermEditor; 050import org.kuali.rice.krms.dto.TermParameterEditor; 051import org.kuali.rice.krms.builder.ComponentBuilder; 052import org.kuali.rice.krms.service.TemplateRegistry; 053import org.kuali.rice.krms.tree.RuleCompareTreeBuilder; 054import org.kuali.rice.krms.tree.RuleViewTreeBuilder; 055import org.kuali.rice.krms.tree.node.RuleEditorTreeNode; 056import org.kuali.rice.krms.service.RuleEditorMaintainable; 057import org.kuali.student.enrollment.class1.krms.dto.EnrolRuleManagementWrapper; 058import org.kuali.student.enrollment.class2.courseoffering.service.decorators.PermissionServiceConstants; 059import org.kuali.rice.krms.util.PropositionTreeUtil; 060import org.kuali.student.enrollment.uif.service.impl.KSMaintainableImpl; 061import org.kuali.student.r2.common.dto.ContextInfo; 062 063import javax.xml.namespace.QName; 064import java.util.ArrayList; 065import java.util.Collection; 066import java.util.HashMap; 067import java.util.List; 068import java.util.Map; 069 070/** 071 * {@link org.kuali.rice.krad.maintenance.Maintainable} for the {@link org.kuali.rice.krms.impl.ui.AgendaEditor} 072 * 073 * @author Kuali Rice Team (rice.collab@kuali.org) 074 */ 075public class RuleEditorMaintainableImpl extends KSMaintainableImpl implements RuleEditorMaintainable { 076 077 private static final long serialVersionUID = 1L; 078 079 private transient RuleManagementService ruleManagementService; 080 private transient KrmsTypeRepositoryService krmsTypeRepositoryService; 081 private transient TermRepositoryService termRepositoryService; 082 083 private transient ContextInfo contextInfo; 084 private transient TemplateRegistry templateRegistry; 085 086 public static final String NEW_AGENDA_EDITOR_DOCUMENT_TEXT = "New Agenda Editor Document"; 087 088 /** 089 * Get the AgendaEditor out of the MaintenanceDocumentForm's newMaintainableObject 090 * 091 * @param model the MaintenanceDocumentForm 092 * @return the AgendaEditor 093 */ 094 private RuleEditor getRuleEditor(Object model) { 095 MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model; 096 return (RuleEditor) maintenanceDocumentForm.getDocument().getNewMaintainableObject().getDataObject(); 097 } 098 099 @Override 100 public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) { 101 EnrolRuleManagementWrapper dataObject = new EnrolRuleManagementWrapper(); 102 103 List<AgendaEditor> agendas = new ArrayList<AgendaEditor>(); 104 dataObject.setAgendas(agendas); 105 106 String coId = dataObjectKeys.get("refObjectId"); 107 dataObject.setRefObjectId(coId); 108 109 dataObject.setCompareTree(RuleCompareTreeBuilder.initCompareTree()); 110 111 return dataObject; 112 } 113 114 protected AgendaEditor getAgendaEditor(String agendaId) { 115 AgendaDefinition agenda = this.getRuleManagementService().getAgenda(agendaId); 116 AgendaEditor agendaEditor = new AgendaEditor(agenda); 117 118 AgendaTreeDefinition agendaTree = this.getRuleManagementService().getAgendaTree(agendaId); 119 agendaEditor.setRuleEditors(getRuleEditorsFromTree(agendaTree.getEntries())); 120 121 return agendaEditor; 122 } 123 124 protected List<RuleEditor> getRuleEditorsFromTree(List<AgendaTreeEntryDefinitionContract> agendaTreeEntries) { 125 126 RuleViewTreeBuilder viewTreeBuilder = new RuleViewTreeBuilder(); 127 viewTreeBuilder.setRuleManagementService(this.getRuleManagementService()); 128 List<RuleEditor> rules = new ArrayList<RuleEditor>(); 129 for (AgendaTreeEntryDefinitionContract treeEntry : agendaTreeEntries) { 130 if (treeEntry instanceof AgendaTreeRuleEntry) { 131 AgendaTreeRuleEntry treeRuleEntry = (AgendaTreeRuleEntry) treeEntry; 132 AgendaItemDefinition agendaItem = this.getRuleManagementService().getAgendaItem(treeEntry.getAgendaItemId()); 133 134 if (agendaItem.getRuleId() != null) { 135 RuleDefinition rule = this.getRuleManagementService().getRule(treeRuleEntry.getRuleId()); 136 RuleEditor ruleEditor = new RuleEditor(rule); 137 this.initPropositionEditor((PropositionEditor) ruleEditor.getProposition()); 138 ruleEditor.setViewTree(viewTreeBuilder.buildTree(ruleEditor)); 139 rules.add(ruleEditor); 140 } 141 142 if (treeRuleEntry.getIfTrue() != null) { 143 rules.addAll(getRuleEditorsFromTree(treeRuleEntry.getIfTrue().getEntries())); 144 } 145 } 146 } 147 return rules; 148 } 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override 154 public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) { 155 super.processAfterNew(document, requestParameters); 156 document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT); 157 } 158 159 @Override 160 public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) { 161 super.processAfterEdit(document, requestParameters); 162 document.getDocumentHeader().setDocumentDescription("Modify Agenda Editor Document"); 163 } 164 165 @Override 166 public void saveDataObject() { 167 RuleManagementWrapper ruleWrapper = (RuleManagementWrapper) getDataObject(); 168 169 for (AgendaEditor agenda : ruleWrapper.getAgendas()){ 170 171 List<RuleEditor> deleteRuleList = new ArrayList<RuleEditor>(); 172 for(RuleEditor rule : agenda.getRuleEditors()) { 173 if(!rule.isDummy()) { 174 this.finRule(rule, ruleWrapper.getNamePrefix(), ruleWrapper.getNamespace()); 175 } else { 176 deleteRuleList.add(rule); 177 } 178 } 179 agenda.getRuleEditors().removeAll(deleteRuleList); 180 181 AgendaDefinition.Builder agendaBuilder = null; 182 AgendaDefinition agendaDefinition = null; 183 if(agenda.getId() == null) { 184 agendaBuilder = AgendaDefinition.Builder.create(agenda); 185 agendaDefinition = agendaBuilder.build(); 186 } 187 188 if(agenda.getRuleEditors().size() == 0) { 189 for(String deletedRuleId : ruleWrapper.getDeletedRuleIds()) { 190 this.getRuleManagementService().deleteAgendaItem(deletedRuleId); 191 } 192 this.getRuleManagementService().deleteReferenceObjectBinding(agenda.getId()); 193 this.getRuleManagementService().deleteAgenda(agenda.getId()); 194 } else { 195 if(agendaDefinition == null) { 196 agendaBuilder = AgendaDefinition.Builder.create(agenda); 197 agendaDefinition = agendaBuilder.build(); 198 } 199 this.getRuleManagementService().updateAgenda(agendaDefinition); 200 } 201 } 202 203 } 204 205 protected void finRule(RuleEditor rule, String rulePrefix, String namespace) { 206 // handle saving new parameterized terms 207 PropositionEditor proposition = (PropositionEditor) rule.getProposition(); 208 if (proposition != null) { 209 this.finPropositionEditor(proposition); 210 } 211 212 if(rule.getName() == null) { 213 rule.setName(rulePrefix + " " + rule.getDescription()); 214 } 215 if(rule.getNamespace() == null) { 216 rule.setNamespace(namespace); 217 } 218 219 RuleDefinition.Builder ruleBuilder = RuleDefinition.Builder.create(rule); 220 RuleDefinition ruleDefinition = ruleBuilder.build(); 221 if (ruleDefinition.getId() == null) { 222 this.getRuleManagementService().createRule(ruleDefinition); 223 } else { 224 this.getRuleManagementService().updateRule(ruleDefinition); 225 } 226 } 227 228 protected void finPropositionEditor(PropositionEditor propositionEditor) { 229 if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) { 230 231 //Save term and set termid. 232 String termId = this.saveTerm(propositionEditor); 233 if (propositionEditor.getParameters().get(0) != null) { 234 propositionEditor.getParameters().get(0).setValue(termId); 235 } 236 237 //Set the default operation and value 238 TemplateInfo template = this.getTemplateRegistry().getTemplateForType(propositionEditor.getType()); 239 propositionEditor.getParameters().get(2).setValue(template.getOperator()); 240 241 if (!"n".equals(template.getValue())) { 242 propositionEditor.getParameters().get(1).setValue(template.getValue()); 243 } 244 245 } else { 246 247 //If not a simple node, recursively finalize the child proposition editors. 248 for (PropositionEditor child : propositionEditor.getCompoundEditors()) { 249 finPropositionEditor(child); 250 } 251 252 } 253 } 254 255 protected String saveTerm(PropositionEditor propositionEditor) { 256 257 //Set the termSpecification based on current type. 258 TermEditor term = propositionEditor.getTerm(); 259 term.setSpecification(this.getTermSpecForType(propositionEditor.getType())); 260 261 TermDefinition.Builder termBuilder = TermDefinition.Builder.create(term); 262 TermDefinition termDefinition = termBuilder.build(); 263 if (term.getId() == null) { 264 termDefinition = this.getTermRepositoryService().createTerm(termDefinition); 265 266 } else { 267 this.getTermRepositoryService().updateTerm(termDefinition); 268 } 269 270 return termDefinition.getId(); 271 } 272 273 protected TermSpecificationDefinition getTermSpecForType(String type) { 274 275 //Get the term output name for this type. 276 String termSpecName = this.getTemplateRegistry().getTermSpecNameForType(type); 277 278 List<TermResolverDefinition> matchingTermResolvers = this.getTermRepositoryService().findTermResolversByNamespace(PermissionServiceConstants.KS_SYS_NAMESPACE); 279 for (TermResolverDefinition termResolver : matchingTermResolvers) { 280 TermSpecificationDefinition termSpec = termResolver.getOutput(); 281 if (termSpec.getName().equals(termSpecName)) { 282 return termSpec; 283 } 284 } 285 286 return null; 287 } 288 289 protected void initPropositionEditor(PropositionEditor propositionEditor) { 290 if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(propositionEditor.getPropositionTypeCode())) { 291 292 if (propositionEditor.getType() == null) { 293 KrmsTypeDefinition type = this.getKrmsTypeRepositoryService().getTypeById(propositionEditor.getTypeId()); 294 propositionEditor.setType(type.getName()); 295 } 296 297 Map<String, String> termParameters = this.getTermParameters(propositionEditor); 298 ComponentBuilder builder = this.getTemplateRegistry().getComponentBuilderForType(propositionEditor.getType()); 299 if (builder != null) { 300 builder.resolveTermParameters(propositionEditor, termParameters); 301 } 302 } else { 303 for (PropositionEditor child : propositionEditor.getCompoundEditors()) { 304 initPropositionEditor(child); 305 } 306 307 } 308 } 309 310 protected Map<String, String> getTermParameters(PropositionEditor proposition) { 311 312 Map<String, String> termParameters = new HashMap<String, String>(); 313 if (proposition.getTerm() == null) { 314 if (proposition.getParameters().get(0) != null) { 315 316 PropositionParameterEditor termParameter = proposition.getParameters().get(0); 317 if (termParameter.getTermValue() == null){ 318 String termId = proposition.getParameters().get(0).getValue(); 319 termParameter.setTermValue(this.getTermRepositoryService().getTerm(termId)); 320 } 321 proposition.setTerm(new TermEditor(termParameter.getTermValue())); 322 } else { 323 return termParameters; 324 } 325 } 326 327 for (TermParameterEditor parameter : proposition.getTerm().getEditorParameters()) { 328 termParameters.put(parameter.getName(), parameter.getValue()); 329 } 330 331 return termParameters; 332 } 333 334 /** 335 * In the case of edit maintenance adds a new blank line to the old side 336 * <p/> 337 * TODO: should this write some sort of missing message on the old side 338 * instead? 339 * 340 * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.View, 341 * org.kuali.rice.krad.uif.container.CollectionGroup, Object, 342 * Object) 343 */ 344 @Override 345 protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) { 346 // Check for maintenance documents in edit but exclude notes 347 if (model instanceof MaintenanceDocumentForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceDocumentForm) model).getMaintenanceAction()) && !(addLine instanceof Note)) { 348 MaintenanceDocumentForm maintenanceDocumentForm = (MaintenanceDocumentForm) model; 349 MaintenanceDocument document = maintenanceDocumentForm.getDocument(); 350 351 // Figure out which rule is being edited 352 RuleEditor rule = getRuleEditor(model); 353 // Figure out which proposition is being edited 354 Tree<RuleEditorTreeNode, String> propositionTree = rule.getEditTree(); 355 Node<RuleEditorTreeNode, String> editedPropositionNode = PropositionTreeUtil.findEditedProposition(propositionTree.getRootElement()); 356 357 // get the old object's collection 358 Collection<Object> oldCollection = ObjectPropertyUtils 359 .getPropertyValue(editedPropositionNode.getData(), 360 collectionGroup.getPropertyName()); 361 362 try { 363 Object blankLine = collectionGroup.getCollectionObjectClass().newInstance(); 364 //Add a blank line to the top of the collection 365 if (oldCollection instanceof List) { 366 ((List) oldCollection).add(0, blankLine); 367 } else { 368 oldCollection.add(blankLine); 369 } 370 } catch (Exception e) { 371 throw new RuntimeException("Unable to create new line instance for old maintenance object", e); 372 } 373 } 374 } 375 376 public RuleManagementService getRuleManagementService() { 377 if (ruleManagementService == null) { 378 ruleManagementService = (RuleManagementService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "ruleManagementService")); 379 } 380 return ruleManagementService; 381 } 382 383 public KrmsTypeRepositoryService getKrmsTypeRepositoryService() { 384 if (krmsTypeRepositoryService == null) { 385 krmsTypeRepositoryService = (KrmsTypeRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "krmsTypeRepositoryService")); 386 } 387 return krmsTypeRepositoryService; 388 } 389 390 public TermRepositoryService getTermRepositoryService() { 391 if (termRepositoryService == null) { 392 termRepositoryService = (TermRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "termRepositoryService")); 393 } 394 return termRepositoryService; 395 } 396 397 private TemplateRegistry getTemplateRegistry() { 398 if (templateRegistry == null) { 399 templateRegistry = (TemplateRegistry) GlobalResourceLoader.getService(QName.valueOf("templateResolverMockService")); 400 } 401 return templateRegistry; 402 } 403}