001/** 002 * Copyright 2005-2017 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.impl.ui; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.apache.commons.lang.StringUtils; 020import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException; 021import org.kuali.rice.core.api.data.DataType; 022import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 023import org.kuali.rice.core.api.uif.RemotableAttributeField; 024import org.kuali.rice.core.api.uif.RemotableTextInput; 025import org.kuali.rice.core.api.util.tree.Node; 026import org.kuali.rice.core.api.util.tree.Tree; 027import org.kuali.rice.core.impl.cache.DistributedCacheManagerDecorator; 028import org.kuali.rice.krad.data.PersistenceOption; 029import org.kuali.rice.krad.maintenance.Maintainable; 030import org.kuali.rice.krad.maintenance.MaintainableImpl; 031import org.kuali.rice.krad.maintenance.MaintenanceDocument; 032import org.kuali.rice.krad.uif.container.Container; 033import org.kuali.rice.krad.uif.view.View; 034import org.kuali.rice.krad.uif.view.ViewModel; 035import org.kuali.rice.krad.util.KRADConstants; 036import org.kuali.rice.krad.util.ObjectUtils; 037import org.kuali.rice.krad.web.form.MaintenanceDocumentForm; 038import org.kuali.rice.krms.api.KrmsConstants; 039import org.kuali.rice.krms.api.repository.action.ActionDefinition; 040import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition; 041import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition; 042import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition; 043import org.kuali.rice.krms.api.repository.context.ContextDefinition; 044import org.kuali.rice.krms.api.repository.function.FunctionDefinition; 045import org.kuali.rice.krms.api.repository.operator.CustomOperator; 046import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition; 047import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType; 048import org.kuali.rice.krms.api.repository.rule.RuleDefinition; 049import org.kuali.rice.krms.api.repository.term.TermDefinition; 050import org.kuali.rice.krms.api.repository.term.TermResolverDefinition; 051import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition; 052import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition; 053import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition; 054import org.kuali.rice.krms.impl.repository.ActionAttributeBo; 055import org.kuali.rice.krms.impl.repository.ActionBo; 056import org.kuali.rice.krms.impl.repository.AgendaBo; 057import org.kuali.rice.krms.impl.repository.AgendaItemBo; 058import org.kuali.rice.krms.impl.repository.ContextBoService; 059import org.kuali.rice.krms.impl.repository.KrmsAttributeDefinitionService; 060import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator; 061import org.kuali.rice.krms.impl.repository.PropositionBo; 062import org.kuali.rice.krms.impl.repository.PropositionParameterBo; 063import org.kuali.rice.krms.impl.repository.RepositoryBoIncrementer; 064import org.kuali.rice.krms.impl.repository.RuleAttributeBo; 065import org.kuali.rice.krms.impl.repository.RuleBo; 066import org.kuali.rice.krms.impl.repository.TermBo; 067import org.kuali.rice.krms.impl.repository.TermParameterBo; 068import org.kuali.rice.krms.impl.util.KrmsImplConstants; 069import org.kuali.rice.krms.impl.util.KrmsRetriever; 070import org.kuali.rice.krms.impl.util.KrmsServiceLocatorInternal; 071 072import java.util.ArrayList; 073import java.util.Collections; 074import java.util.Date; 075import java.util.HashMap; 076import java.util.List; 077import java.util.Map; 078 079import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findSingleMatching; 080 081/** 082 * {@link Maintainable} for the {@link AgendaEditor} 083 * 084 * @author Kuali Rice Team (rice.collab@kuali.org) 085 */ 086public class AgendaEditorMaintainable extends MaintainableImpl { 087 088 private static final long serialVersionUID = 1L; 089 090 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger( 091 AgendaEditorMaintainable.class); 092 093 public static final String NEW_AGENDA_EDITOR_DOCUMENT_TEXT = "New Agenda Editor Document"; 094 private static final RepositoryBoIncrementer termIdIncrementer = new RepositoryBoIncrementer(TermBo.TERM_SEQ_NAME); 095 private static final RepositoryBoIncrementer termParameterIdIncrementer = new RepositoryBoIncrementer(TermParameterBo.TERM_PARM_SEQ_NAME); 096 private static final RepositoryBoIncrementer agendaItemIncrementer = new RepositoryBoIncrementer(AgendaBo.AGENDA_SEQ_NAME); 097 098 private transient KrmsRetriever krmsRetriever = new KrmsRetriever(); 099 100 /** 101 * return the contextBoService 102 */ 103 private ContextBoService getContextBoService() { 104 return KrmsRepositoryServiceLocator.getContextBoService(); 105 } 106 107 public List<RemotableAttributeField> retrieveAgendaCustomAttributes(View view, Object model, Container container) { 108 AgendaEditor agendaEditor = getAgendaEditor(model); 109 return krmsRetriever.retrieveAgendaCustomAttributes(agendaEditor); 110 } 111 112 /** 113 * Retrieve a list of {@link RemotableAttributeField}s for the parameters (if any) required by the resolver for 114 * the selected term in the proposition that is under edit. 115 */ 116 public List<RemotableAttributeField> retrieveTermParameters(View view, Object model, Container container) { 117 118 List<RemotableAttributeField> results = new ArrayList<RemotableAttributeField>(); 119 120 AgendaEditor agendaEditor = getAgendaEditor(model); 121 122 // Figure out which rule is being edited 123 RuleBo rule = agendaEditor.getAgendaItemLine().getRule(); 124 if (null != rule) { 125 126 // Figure out which proposition is being edited 127 Tree<RuleTreeNode, String> propositionTree = rule.getPropositionTree(); 128 Node<RuleTreeNode, String> editedPropositionNode = findEditedProposition(propositionTree.getRootElement()); 129 130 if (editedPropositionNode != null) { 131 PropositionBo propositionBo = editedPropositionNode.getData().getProposition(); 132 if (StringUtils.isEmpty(propositionBo.getCompoundOpCode()) && CollectionUtils.size( 133 propositionBo.getParameters()) > 0) { 134 // Get the term ID; if it is a new parameterized term, it will have a special prefix 135 PropositionParameterBo param = propositionBo.getParameters().get(0); 136 if (StringUtils.isNotBlank(param.getValue()) && 137 param.getValue().startsWith(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) { 138 String termSpecId = param.getValue().substring( 139 KrmsImplConstants.PARAMETERIZED_TERM_PREFIX.length()); 140 TermResolverDefinition simplestResolver = getSimplestTermResolver(termSpecId, 141 rule.getNamespace()); 142 143 // Get the parameters and build RemotableAttributeFields 144 if (simplestResolver != null) { 145 List<String> parameterNames = new ArrayList<String>(simplestResolver.getParameterNames()); 146 Collections.sort(parameterNames); // make param order deterministic 147 148 for (String parameterName : parameterNames) { 149 // TODO: also allow for DD parameters if there are matching type attributes 150 RemotableTextInput.Builder controlBuilder = RemotableTextInput.Builder.create(); 151 controlBuilder.setSize(64); 152 153 RemotableAttributeField.Builder builder = RemotableAttributeField.Builder.create( 154 parameterName); 155 156 builder.setRequired(true); 157 builder.setDataType(DataType.STRING); 158 builder.setControl(controlBuilder); 159 builder.setLongLabel(parameterName); 160 builder.setShortLabel(parameterName); 161 builder.setMinLength(Integer.valueOf(1)); 162 builder.setMaxLength(Integer.valueOf(64)); 163 164 results.add(builder.build()); 165 } 166 } 167 } 168 } 169 } 170 } 171 return results; 172 } 173 174 /** 175 * finds the term resolver with the fewest parameters that resolves the given term specification 176 * 177 * @param termSpecId the id of the term specification 178 * @param namespace the namespace of the term specification 179 * @return the simples {@link TermResolverDefinition} found, or null if none was found 180 */ 181 // package access so that AgendaEditorController can use it too 182 static TermResolverDefinition getSimplestTermResolver(String termSpecId, 183 String namespace) {// Get the term resolver for the term spec 184 185 List<TermResolverDefinition> resolvers = 186 KrmsRepositoryServiceLocator.getTermBoService().findTermResolversByOutputId(termSpecId, namespace); 187 188 TermResolverDefinition simplestResolver = null; 189 190 for (TermResolverDefinition resolver : resolvers) { 191 if (simplestResolver == null || simplestResolver.getParameterNames().size() < resolver.getParameterNames() 192 .size()) { 193 simplestResolver = resolver; 194 } 195 } 196 197 return simplestResolver; 198 } 199 200 /** 201 * Find and return the node containing the proposition that is in currently in edit mode 202 * 203 * @param node the node to start searching from (typically the root) 204 * @return the node that is currently being edited, if any. Otherwise, null. 205 */ 206 private Node<RuleTreeNode, String> findEditedProposition(Node<RuleTreeNode, String> node) { 207 Node<RuleTreeNode, String> result = null; 208 if (node.getData() != null && node.getData().getProposition() != null && node.getData().getProposition() 209 .getEditMode()) { 210 result = node; 211 } else { 212 for (Node<RuleTreeNode, String> child : node.getChildren()) { 213 result = findEditedProposition(child); 214 if (result != null) { 215 break; 216 } 217 } 218 } 219 return result; 220 } 221 222 /** 223 * Get the AgendaEditor out of the MaintenanceDocumentForm's newMaintainableObject 224 * 225 * @param model the MaintenanceDocumentForm 226 * @return the AgendaEditor 227 */ 228 private AgendaEditor getAgendaEditor(Object model) { 229 MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model; 230 return (AgendaEditor) maintenanceForm.getDocument().getNewMaintainableObject().getDataObject(); 231 } 232 233 public List<RemotableAttributeField> retrieveRuleActionCustomAttributes(View view, Object model, 234 Container container) { 235 AgendaEditor agendaEditor = getAgendaEditor((MaintenanceDocumentForm) model); 236 return krmsRetriever.retrieveRuleActionCustomAttributes(agendaEditor); 237 } 238 239 /** 240 * This only supports a single action within a rule. 241 */ 242 public List<RemotableAttributeField> retrieveRuleCustomAttributes(View view, Object model, Container container) { 243 AgendaEditor agendaEditor = getAgendaEditor((MaintenanceDocumentForm) model); 244 return krmsRetriever.retrieveRuleCustomAttributes(agendaEditor); 245 } 246 247 @Override 248 public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) { 249 Object dataObject = null; 250 251 try { 252 // Since the dataObject is a wrapper class we need to build it and populate with the agenda bo. 253 AgendaEditor agendaEditor = new AgendaEditor(); 254 AgendaBo agenda = findSingleMatching(getDataObjectService(), 255 ((AgendaEditor) getDataObject()).getAgenda().getClass(), dataObjectKeys); 256 257 // HACK: force lazy loaded items to be fetched 258 forceLoadLazyRelations(agenda); 259 260 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(getMaintenanceAction())) { 261 String dateTimeStamp = (new Date()).getTime() + ""; 262 String newAgendaName = AgendaItemBo.COPY_OF_TEXT + agenda.getName() + " " + dateTimeStamp; 263 264 AgendaBo copiedAgenda = agenda.copyAgenda(newAgendaName, dateTimeStamp); 265 266 document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT); 267 document.setFieldsClearedOnCopy(true); 268 agendaEditor.setAgenda(copiedAgenda); 269 } else { 270 // set custom attributes map in AgendaEditor 271 // agendaEditor.setCustomAttributesMap(agenda.getAttributes()); 272 agendaEditor.setAgenda(agenda); 273 } 274 agendaEditor.setCustomAttributesMap(agenda.getAttributes()); 275 276 // set extra fields on AgendaEditor 277 agendaEditor.setNamespace(agenda.getContext().getNamespace()); 278 agendaEditor.setContextName(agenda.getContext().getName()); 279 280 dataObject = agendaEditor; 281 } catch (ClassNotPersistenceCapableException ex) { 282 if (!document.getOldMaintainableObject().isExternalBusinessObject()) { 283 throw new RuntimeException("Data Object Class: " 284 + getDataObjectClass() 285 + " is not persistable and is not externalizable - configuration error"); 286 } 287 // otherwise, let fall through 288 } 289 290 return dataObject; 291 } 292 293 private void forceLoadLazyRelations(AgendaBo agenda) { 294 for (AgendaItemBo item : agenda.getItems()) { 295 for (ActionBo action : item.getRule().getActions()) { 296 if (CollectionUtils.isEmpty(action.getAttributeBos())) { continue; } 297 298 for (ActionAttributeBo actionAttribute : action.getAttributeBos()) { 299 actionAttribute.getAttributeDefinition(); 300 } 301 } 302 303 Tree propTree = item.getRule().refreshPropositionTree(true); 304 walkPropositionTree(item.getRule().getProposition()); 305 306 for (RuleAttributeBo ruleAttribute : item.getRule().getAttributeBos()) { 307 ruleAttribute.getAttributeDefinition(); 308 } 309 } 310 } 311 312 private void walkPropositionTree(PropositionBo prop) { 313 if (prop == null) { return; } 314 315 if (prop.getParameters() != null) for (PropositionParameterBo param : prop.getParameters()) { 316 param.getPropId(); 317 } 318 319 if (prop.getCompoundComponents() != null) for (PropositionBo childProp : prop.getCompoundComponents()) { 320 walkPropositionTree(childProp); 321 } 322 } 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override 328 public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) { 329 super.processAfterNew(document, requestParameters); 330 document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT); 331 } 332 333 @Override 334 public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) { 335 super.processAfterEdit(document, requestParameters); 336 document.getDocumentHeader().setDocumentDescription("Modify Agenda Editor Document"); 337 } 338 339 @Override 340 public void processAfterCopy(MaintenanceDocument document, 341 Map<String, String[]> parameters) { 342 super.processAfterCopy(document, parameters); 343 AgendaBo agendaBo = ((AgendaEditor) document.getDocumentDataObject()).getAgenda(); 344 agendaBo.setVersionNumber(null); 345 346 for (AgendaItemBo agendaItem : agendaBo.getItems()) { 347 agendaItem.setVersionNumber(null); 348 } 349 } 350 @Override 351 public void prepareForSave() { 352 // set agenda attributes 353 AgendaEditor agendaEditor = (AgendaEditor) getDataObject(); 354 agendaEditor.getAgenda().setAttributes(agendaEditor.getCustomAttributesMap()); 355 } 356 357 @Override 358 public void saveDataObject() { 359 AgendaBo agendaBo = ((AgendaEditor) getDataObject()).getAgenda(); 360 361 AgendaItemBo firstItem = null; 362 363 // Find the first agenda item 364 for (AgendaItemBo agendaItem : agendaBo.getItems()) { 365 if (agendaBo.getFirstItemId().equals(agendaItem.getId())) { 366 firstItem = agendaItem; 367 } 368 } 369 370 // if new agenda persist without items. This works around a chicken and egg problem 371 // with KRMS_AGENDA_T.INIT_AGENDA_ITM_ID and KRMS_AGENDA_ITM_T.AGENDA_ITM_ID both being non-nullable 372 List<AgendaItemBo> agendaItems = agendaBo.getItems(); 373 List<AgendaItemBo> updatedItems = new ArrayList<AgendaItemBo>(); 374 List<AgendaItemBo> deletedItems = new ArrayList<AgendaItemBo>(); 375 AgendaBo existing = null; 376 377 if (agendaBo.getId() != null) { 378 existing = getDataObjectService().find(AgendaBo.class, agendaBo.getId()); 379 } 380 381 if (existing == null) { 382 agendaBo.setItems(updatedItems); 383 agendaBo.setFirstItem(null); 384 agendaBo = getDataObjectService().save(agendaBo); 385 getDataObjectService().flush(AgendaBo.class); 386 String agendaBoId = agendaBo.getId(); 387 agendaBo = getDataObjectService().find(AgendaBo.class,agendaBoId); 388 agendaBo.setItems(agendaItems); 389 agendaBo.setFirstItem(firstItem); 390 } else { 391 // Create a list of agendaItems that will be used to delete rules when the data object is saved 392 for (AgendaItemBo existingAgendaItem : existing.getItems()) { 393 boolean deletedAgendaItem = true; 394 for (AgendaItemBo agendaItem : agendaBo.getItems()) { 395 if (agendaItem.getId().equalsIgnoreCase(existingAgendaItem.getId())) { 396 deletedAgendaItem = false; 397 break; 398 } 399 } 400 if (deletedAgendaItem) { 401 deletedItems.add(existingAgendaItem); 402 } 403 } 404 } 405 406 // handle saving new parameterized terms and processing custom operators 407 for (AgendaItemBo agendaItem : agendaBo.getItems()) { 408 PropositionBo propositionBo = agendaItem.getRule().getProposition(); 409 if (propositionBo != null) { 410 saveNewParameterizedTerms(propositionBo); 411 processCustomOperators(propositionBo); 412 } 413 } 414 415 if (agendaBo != null) { 416 flushCacheBeforeSave(); 417 getDataObjectService().flush(AgendaBo.class); 418 419 // Need to set the first item for persistence to cascade 420 agendaBo.setFirstItem(firstItem); 421 getDataObjectService().save(agendaBo); 422 423 // delete orphaned propositions, rules, etc. 424 for (String deletedPropId : ((AgendaEditor) getDataObject()).getDeletedPropositionIds()) { 425 PropositionBo toDelete = getDataObjectService().find(PropositionBo.class, deletedPropId); 426 427 getDataObjectService().delete(toDelete); 428 } 429 430 for (AgendaItemBo deletedItem : deletedItems) { 431 if (deletedItem.getRule() != null) { 432 getDataObjectService().delete(deletedItem.getRule()); 433 } 434 } 435 } else { 436 throw new RuntimeException("Cannot save null " + AgendaBo.class.getName() + " with business object service"); 437 } 438 } 439 440 private void flushCacheBeforeSave(){ 441 //flush krms caches 442 DistributedCacheManagerDecorator distributedCacheManagerDecorator = 443 GlobalResourceLoader.getService(KrmsConstants.KRMS_DISTRIBUTED_CACHE); 444 445 distributedCacheManagerDecorator.getCache(ActionDefinition.Cache.NAME).clear(); 446 distributedCacheManagerDecorator.getCache(AgendaItemDefinition.Cache.NAME).clear(); 447 distributedCacheManagerDecorator.getCache(AgendaTreeDefinition.Cache.NAME).clear(); 448 distributedCacheManagerDecorator.getCache(AgendaDefinition.Cache.NAME).clear(); 449 distributedCacheManagerDecorator.getCache(ContextDefinition.Cache.NAME).clear(); 450 distributedCacheManagerDecorator.getCache(KrmsAttributeDefinition.Cache.NAME).clear(); 451 distributedCacheManagerDecorator.getCache(KrmsTypeDefinition.Cache.NAME).clear(); 452 distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear(); 453 distributedCacheManagerDecorator.getCache(PropositionDefinition.Cache.NAME).clear(); 454 distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear(); 455 distributedCacheManagerDecorator.getCache(TermDefinition.Cache.NAME).clear(); 456 distributedCacheManagerDecorator.getCache(TermResolverDefinition.Cache.NAME).clear(); 457 distributedCacheManagerDecorator.getCache(TermSpecificationDefinition.Cache.NAME).clear(); 458 } 459 460 /** 461 * walk the proposition tree and save any new parameterized terms that are contained therein 462 * 463 * @param propositionBo the root proposition from which to search 464 */ 465 private void saveNewParameterizedTerms(PropositionBo propositionBo) { 466 if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) { 467 // it is a simple proposition 468 if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(0).getValue().startsWith( 469 KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) { 470 String termId = propositionBo.getParameters().get(0).getValue(); 471 String termSpecId = termId.substring(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX.length()); 472 // create new term 473 TermBo newTerm = new TermBo(); 474 newTerm.setDescription(propositionBo.getNewTermDescription()); 475 newTerm.setSpecificationId(termSpecId); 476 newTerm.setId(termIdIncrementer.getNewId()); 477 478 List<TermParameterBo> params = new ArrayList<TermParameterBo>(); 479 for (Map.Entry<String, String> entry : propositionBo.getTermParameters().entrySet()) { 480 TermParameterBo param = new TermParameterBo(); 481 param.setTerm(newTerm); 482 param.setName(entry.getKey()); 483 param.setValue(entry.getValue()); 484 param.setId(termParameterIdIncrementer.getNewId()); 485 486 params.add(param); 487 } 488 489 newTerm.setParameters(params); 490 491 getLegacyDataAdapter().linkAndSave(newTerm); 492 propositionBo.getParameters().get(0).setValue(newTerm.getId()); 493 } 494 } else { 495 // recurse 496 for (PropositionBo childProp : propositionBo.getCompoundComponents()) { 497 saveNewParameterizedTerms(childProp); 498 } 499 } 500 } 501 502 /** 503 * walk the proposition tree and process any custom operators found, converting them to custom function invocations. 504 * 505 * @param propositionBo the root proposition from which to search and convert 506 */ 507 private void processCustomOperators(PropositionBo propositionBo) { 508 if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) { 509 // if it is a simple proposition with a custom operator 510 if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(2).getValue().startsWith( 511 KrmsImplConstants.CUSTOM_OPERATOR_PREFIX)) { 512 PropositionParameterBo operatorParam = propositionBo.getParameters().get(2); 513 514 CustomOperator customOperator = 515 KrmsServiceLocatorInternal.getCustomOperatorUiTranslator().getCustomOperator(operatorParam.getValue()); 516 517 FunctionDefinition operatorFunctionDefinition = customOperator.getOperatorFunctionDefinition(); 518 519 operatorParam.setParameterType(PropositionParameterType.FUNCTION.getCode()); 520 operatorParam.setValue(operatorFunctionDefinition.getId()); 521 } 522 } else { 523 // recurse 524 for (PropositionBo childProp : propositionBo.getCompoundComponents()) { 525 processCustomOperators(childProp); 526 } 527 } 528 } 529 530 /** 531 * Build a map from attribute name to attribute definition from all the defined attribute definitions for the 532 * specified agenda type 533 * 534 * @param agendaTypeId 535 * @return 536 */ 537 private Map<String, KrmsAttributeDefinition> buildAttributeDefinitionMap(String agendaTypeId) { 538 KrmsAttributeDefinitionService attributeDefinitionService = 539 KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService(); 540 541 // build a map from attribute name to definition 542 Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>(); 543 544 List<KrmsAttributeDefinition> attributeDefinitions = attributeDefinitionService.findAttributeDefinitionsByType( 545 agendaTypeId); 546 547 for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) { 548 attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition); 549 } 550 return attributeDefinitionMap; 551 } 552 553 @Override 554 public boolean isOldDataObjectInDocument() { 555 boolean isOldDataObjectInExistence = true; 556 557 if (getDataObject() == null) { 558 isOldDataObjectInExistence = false; 559 } else { 560 // dataObject contains a non persistable wrapper - use agenda from the wrapper object instead 561 Map<String, ?> keyFieldValues = getLegacyDataAdapter().getPrimaryKeyFieldValues( 562 ((AgendaEditor) getDataObject()).getAgenda()); 563 for (Object keyValue : keyFieldValues.values()) { 564 if (keyValue == null) { 565 isOldDataObjectInExistence = false; 566 } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) { 567 isOldDataObjectInExistence = false; 568 } 569 570 if (!isOldDataObjectInExistence) { 571 break; 572 } 573 } 574 } 575 576 return isOldDataObjectInExistence; 577 } 578 579 // Since the dataObject is a wrapper class we need to return the agendaBo instead. 580 @Override 581 public Class getDataObjectClass() { 582 return AgendaBo.class; 583 } 584 585 @Override 586 public boolean isLockable() { 587 return true; 588 } 589 590 @Override 591 public void processBeforeAddLine(ViewModel model, Object addLine, String collectionId, String collectionPath) { 592 AgendaEditor agendaEditor = getAgendaEditor(model); 593 if (addLine instanceof ActionBo) { 594 ((ActionBo) addLine).setNamespace(agendaEditor.getAgendaItemLine().getRule().getNamespace()); 595 } 596 597 super.processBeforeAddLine(model, addLine, collectionId, collectionPath); 598 } 599}