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.repository; 017 018import org.apache.commons.lang.StringUtils; 019import org.eclipse.persistence.annotations.PrivateOwned; 020import org.kuali.rice.core.api.mo.common.Versioned; 021import org.kuali.rice.krad.data.DataObjectService; 022import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator; 023import org.kuali.rice.krad.service.KRADServiceLocator; 024import org.kuali.rice.krad.uif.util.ScriptUtils; 025import org.kuali.rice.krms.api.repository.LogicalOperator; 026import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition; 027import org.kuali.rice.krms.api.repository.proposition.PropositionDefinitionContract; 028import org.kuali.rice.krms.api.repository.proposition.PropositionParameter; 029import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType; 030import org.kuali.rice.krms.api.repository.proposition.PropositionType; 031import org.kuali.rice.krms.impl.ui.CustomOperatorUiTranslator; 032import org.kuali.rice.krms.impl.ui.TermParameter; 033import org.kuali.rice.krms.impl.util.KrmsImplConstants; 034import org.kuali.rice.krms.impl.util.KrmsServiceLocatorInternal; 035 036import javax.persistence.CascadeType; 037import javax.persistence.Column; 038import javax.persistence.Entity; 039import javax.persistence.GeneratedValue; 040import javax.persistence.Id; 041import javax.persistence.JoinColumn; 042import javax.persistence.JoinTable; 043import javax.persistence.OneToMany; 044import javax.persistence.OrderBy; 045import javax.persistence.Table; 046import javax.persistence.Transient; 047import javax.persistence.Version; 048import java.io.Serializable; 049import java.util.ArrayList; 050import java.util.HashMap; 051import java.util.List; 052import java.util.Map; 053import java.util.UUID; 054 055@Entity 056@Table(name = "KRMS_PROP_T") 057public class PropositionBo implements PropositionDefinitionContract, Versioned, Serializable { 058 059 private static final long serialVersionUID = 1l; 060 061 private static final String PROP_SEQ_NAME = "KRMS_PROP_S"; 062 static final RepositoryBoIncrementer propositionIdIncrementer = new RepositoryBoIncrementer(PROP_SEQ_NAME); 063 static final RepositoryBoIncrementer propositionParameterIdIncrementer = new RepositoryBoIncrementer("KRMS_PROP_PARM_S"); 064 065 @PortableSequenceGenerator(name = PROP_SEQ_NAME) 066 @GeneratedValue(generator = PROP_SEQ_NAME) 067 @Id 068 @Column(name = "PROP_ID") 069 private String id; 070 071 @Column(name = "DESC_TXT") 072 private String description; 073 074 @Column(name = "RULE_ID") 075 private String ruleId; 076 077 @Column(name = "TYP_ID") 078 private String typeId; 079 080 @Column(name = "DSCRM_TYP_CD") 081 private String propositionTypeCode; 082 083 @PrivateOwned 084 @OneToMany(orphanRemoval = true, targetEntity = PropositionParameterBo.class, mappedBy = "proposition", 085 cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE }) 086 @OrderBy("sequenceNumber") 087 private List<PropositionParameterBo> parameters = new ArrayList<PropositionParameterBo>(); 088 089 @Column(name = "CMPND_OP_CD") 090 private String compoundOpCode; 091 092 @Column(name = "CMPND_SEQ_NO") 093 private Integer compoundSequenceNumber; 094 095 @Column(name = "VER_NBR") 096 @Version 097 private Long versionNumber; 098 099 @OneToMany(targetEntity = PropositionBo.class, cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE}) 100 @JoinTable(name = "KRMS_CMPND_PROP_PROPS_T", joinColumns = { @JoinColumn(name = "CMPND_PROP_ID", referencedColumnName = "PROP_ID") }, inverseJoinColumns = { @JoinColumn(name = "PROP_ID", referencedColumnName = "PROP_ID") }) 101 @OrderBy("compoundSequenceNumber") 102 private List<PropositionBo> compoundComponents; 103 104 @Transient 105 private String parameterDisplayString; 106 107 @Transient 108 private boolean editMode = false; 109 110 @Transient 111 private String categoryId; 112 113 @Transient 114 private String termSpecId; 115 116 @Transient 117 private boolean showCustomValue; 118 119 @Transient 120 private String termParameter; 121 122 @Transient 123 private List<TermParameter> termParameterList = new ArrayList<TermParameter>(); 124 125 @Transient 126 private String newTermDescription = "new term " + UUID.randomUUID().toString(); 127 128 @Transient 129 private Map<String, String> termParameters = new HashMap<String, String>(); 130 131 private void setupParameterDisplayString() { 132 if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(getPropositionTypeCode())) { 133 // Simple Propositions should have 3 parameters ordered in reverse polish notation. 134 // TODO: enhance to get term names for term type parameters. 135 List<PropositionParameterBo> parameters = getParameters(); 136 137 if (parameters != null && parameters.size() == 3) { 138 StringBuilder sb = new StringBuilder(); 139 String valueDisplay = getParamValue(parameters.get(1)); 140 141 String lhs = getParamValue(parameters.get(0)); 142 String opr = getParamValue(parameters.get(2)); 143 144 // Since we don't want the operator to be escaped by KRAD. We need to escape the lhs and rhs here 145 // so that we can safely display the parameterString as is. 146 sb.append( ScriptUtils.escapeHtml(lhs) ).append(" ").append( opr ); 147 148 if (opr != null && (opr.equals("!=null") || opr.equals("=null"))){ 149 valueDisplay = null; 150 } 151 152 if (valueDisplay != null) { 153 // !=null and =null operators values will be null and should not be displayed 154 sb.append(" ").append(ScriptUtils.escapeHtml( valueDisplay )); 155 } 156 157 setParameterDisplayString(sb.toString()); 158 } else { 159 // should not happen 160 } 161 } 162 } 163 164 /** 165 * returns the string summary value for the given proposition parameter. 166 * 167 * @param param the proposition parameter to get the summary value for 168 * @return the summary value 169 */ 170 private String getParamValue(PropositionParameterBo param) { 171 CustomOperatorUiTranslator customOperatorUiTranslator = 172 KrmsServiceLocatorInternal.getCustomOperatorUiTranslator(); 173 174 if (PropositionParameterType.TERM.getCode().equalsIgnoreCase(param.getParameterType())) { 175 String termName = ""; 176 String termId = param.getValue(); 177 178 if (termId != null && termId.length() > 0) { 179 if (termId.startsWith(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) { 180 if (!StringUtils.isBlank(newTermDescription)) { 181 termName = newTermDescription; 182 } else { 183 TermSpecificationBo termSpec = getDataObjectService().find(TermSpecificationBo.class, 184 termId.substring(1 + termId.indexOf(":"))); 185 termName = termSpec.getName() + "(...)"; 186 } 187 } else { 188 TermBo term = getDataObjectService().find(TermBo.class, termId); 189 termName = term.getSpecification().getName(); 190 } 191 } 192 193 return termName; 194 195 } else if (PropositionParameterType.FUNCTION.getCode().equalsIgnoreCase(param.getParameterType()) || 196 PropositionParameterType.OPERATOR.getCode().equalsIgnoreCase(param.getParameterType())) { 197 if (customOperatorUiTranslator.isCustomOperatorFormValue(param.getValue())) { 198 String functionName = customOperatorUiTranslator.getCustomOperatorName(param.getValue()); 199 if (!StringUtils.isEmpty(functionName)) { 200 return functionName; 201 } 202 } 203 } 204 205 return param.getValue(); 206 } 207 208 /** 209 * @return the parameterDisplayString 210 */ 211 public String getParameterDisplayString() { 212 setupParameterDisplayString(); 213 214 return this.parameterDisplayString; 215 } 216 217 /** 218 * @param parameterDisplayString the parameterDisplayString to set 219 */ 220 public void setParameterDisplayString(String parameterDisplayString) { 221 this.parameterDisplayString = parameterDisplayString; 222 } 223 224 public boolean getEditMode() { 225 return this.editMode; 226 } 227 228 public void setEditMode(boolean editMode) { 229 this.editMode = editMode; 230 } 231 232 public String getCategoryId() { 233 return this.categoryId; 234 } 235 236 public void setCategoryId(String categoryId) { 237 this.categoryId = categoryId; 238 } 239 240 /** 241 * set the typeId. If the parameter is blank, then this PropositionBo's 242 * typeId will be set to null 243 * 244 * @param typeId 245 */ 246 public void setTypeId(String typeId) { 247 if (StringUtils.isBlank(typeId)) { 248 this.typeId = null; 249 } else { 250 this.typeId = typeId; 251 } 252 } 253 254 @Override 255 public Long getVersionNumber() { 256 return versionNumber; 257 } 258 259 public void setVersionNumber(Long versionNumber) { 260 this.versionNumber = versionNumber; 261 } 262 263 public Map<String, String> getTermParameters() { 264 return termParameters; 265 } 266 267 public void setTermParameters(Map<String, String> termParameters) { 268 this.termParameters = termParameters; 269 } 270 271 public DataObjectService getDataObjectService() { 272 return KRADServiceLocator.getDataObjectService(); 273 } 274 275 /** 276 * Converts a mutable bo to it's immutable counterpart 277 * 278 * @param bo the mutable business object 279 * @return the immutable object 280 */ 281 public static PropositionDefinition to(PropositionBo bo) { 282 if (bo == null) { 283 return null; 284 } 285 286 return PropositionDefinition.Builder.create(bo).build(); 287 } 288 289 /** 290 * Converts a immutable object to it's mutable bo counterpart 291 * 292 * @param im immutable object 293 * @return the mutable bo 294 */ 295 public static PropositionBo from(PropositionDefinition im) { 296 if (im == null) { 297 return null; 298 } 299 300 PropositionBo bo = new PropositionBo(); 301 bo.id = im.getId(); 302 bo.description = im.getDescription(); 303 304 // we don't set rule here, it is set in RuleBo.from 305 306 bo.setRuleId(im.getRuleId()); 307 308 bo.typeId = im.getTypeId(); 309 bo.propositionTypeCode = im.getPropositionTypeCode(); 310 bo.parameters = new ArrayList<PropositionParameterBo>(); 311 312 for (PropositionParameter parm : im.getParameters()) { 313 PropositionParameterBo parmBo = PropositionParameterBo.from(parm); 314 bo.parameters.add(parmBo); 315 parmBo.setProposition(bo); 316 } 317 318 bo.compoundOpCode = im.getCompoundOpCode(); 319 bo.compoundSequenceNumber = im.getCompoundSequenceNumber(); 320 bo.compoundComponents = new ArrayList<PropositionBo>(); 321 322 if (im.getCompoundComponents() != null) { 323 for (PropositionDefinition prop : im.getCompoundComponents()) { 324 bo.compoundComponents.add(PropositionBo.from(prop)); 325 } 326 } 327 328 bo.versionNumber = im.getVersionNumber(); 329 330 return bo; 331 } 332 333 /** 334 * This method creates a partially populated Simple PropositionBo with 335 * three parameters: a term type paramter (value not assigned) 336 * a operation parameter 337 * a constant parameter (value set to empty string) 338 * The returned PropositionBo has an generatedId. The type code and ruleId properties are assigned the 339 * same value as the sibling param passed in. 340 * Each PropositionParameter has the id generated, and type, sequenceNumber, 341 * propId default values set. The value is set to "". 342 * 343 * @param sibling - 344 * @param pType 345 * @return a PropositionBo partially populated. 346 */ 347 public static PropositionBo createSimplePropositionBoStub(PropositionBo sibling, String pType) { 348 // create a simple proposition Bo 349 PropositionBo prop = null; 350 if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(pType)) { 351 prop = new PropositionBo(); 352 prop.setId(propositionIdIncrementer.getNewId()); 353 prop.setPropositionTypeCode(pType); 354 prop.setEditMode(true); 355 356 if (sibling != null) { 357 prop.setRuleId(sibling.getRuleId()); 358 } 359 360 // create blank proposition parameters 361 PropositionParameterBo pTerm = new PropositionParameterBo(); 362 pTerm.setId(propositionParameterIdIncrementer.getNewId()); 363 pTerm.setParameterType("T"); 364 pTerm.setProposition(prop); 365 pTerm.setSequenceNumber(new Integer("0")); 366 pTerm.setVersionNumber(new Long(1)); 367 pTerm.setValue(""); 368 369 // create blank proposition parameters 370 PropositionParameterBo pOp = new PropositionParameterBo(); 371 pOp.setId(propositionParameterIdIncrementer.getNewId()); 372 pOp.setParameterType("O"); 373 pOp.setProposition(prop); 374 pOp.setSequenceNumber(new Integer("2")); 375 pOp.setVersionNumber(new Long(1)); 376 377 // create blank proposition parameters 378 PropositionParameterBo pConst = new PropositionParameterBo(); 379 pConst.setId(propositionParameterIdIncrementer.getNewId()); 380 pConst.setParameterType("C"); 381 pConst.setProposition(prop); 382 pConst.setSequenceNumber(new Integer("1")); 383 pConst.setVersionNumber(new Long(1)); 384 pConst.setValue(""); 385 386 List<PropositionParameterBo> paramList = new ArrayList<PropositionParameterBo>(3); 387 paramList.add(pTerm); 388 paramList.add(pConst); 389 paramList.add(pOp); 390 391 prop.setParameters(paramList); 392 } 393 394 return prop; 395 } 396 397 public static PropositionBo createCompoundPropositionBoStub(PropositionBo existing, boolean addNewChild) { 398 // create a simple proposition Bo 399 PropositionBo prop = new PropositionBo(); 400 prop.setId(propositionIdIncrementer.getNewId()); 401 prop.setPropositionTypeCode(PropositionType.COMPOUND.getCode()); 402 prop.setCompoundOpCode(LogicalOperator.AND.getCode()); 403 404 // default to and 405 prop.setDescription(""); 406 prop.setEditMode(true); 407 408 if (existing != null) { 409 prop.setRuleId(existing.getRuleId()); 410 } 411 412 List<PropositionBo> components = new ArrayList<PropositionBo>(2); 413 components.add(existing); 414 415 if (addNewChild) { 416 PropositionBo newProp = createSimplePropositionBoStub(existing, PropositionType.SIMPLE.getCode()); 417 components.add(newProp); 418 prop.setEditMode(false); 419 } 420 421 prop.setCompoundComponents(components); 422 423 return prop; 424 } 425 426 public static PropositionBo createCompoundPropositionBoStub2(PropositionBo existing) { 427 // create a simple proposition Bo 428 PropositionBo prop = new PropositionBo(); 429 prop.setId(propositionIdIncrementer.getNewId()); 430 prop.setPropositionTypeCode(PropositionType.COMPOUND.getCode()); 431 prop.setRuleId(existing.getRuleId()); 432 prop.setCompoundOpCode(LogicalOperator.AND.getCode()); 433 // default to and 434 prop.setDescription(""); 435 prop.setEditMode(true); 436 List<PropositionBo> components = new ArrayList<PropositionBo>(); 437 ((ArrayList<PropositionBo>) components).add(existing); 438 prop.setCompoundComponents(components); 439 440 return prop; 441 } 442 443 public static PropositionBo copyProposition(PropositionBo existing) { 444 // Note: RuleId is not set 445 PropositionBo newProp = new PropositionBo(); 446 newProp.setId(propositionIdIncrementer.getNewId()); 447 newProp.setDescription(existing.getDescription()); 448 newProp.setPropositionTypeCode(existing.getPropositionTypeCode()); 449 newProp.setTypeId(existing.getTypeId()); 450 newProp.setCompoundOpCode(existing.getCompoundOpCode()); 451 newProp.setCompoundSequenceNumber(existing.getCompoundSequenceNumber()); 452 453 // parameters 454 List<PropositionParameterBo> newParms = new ArrayList<PropositionParameterBo>(); 455 456 for (PropositionParameterBo parm : existing.getParameters()) { 457 PropositionParameterBo p = new PropositionParameterBo(); 458 p.setId(propositionParameterIdIncrementer.getNewId()); 459 p.setParameterType(parm.getParameterType()); 460 p.setProposition(newProp); 461 p.setSequenceNumber(parm.getSequenceNumber()); 462 p.setValue(parm.getValue()); 463 ((ArrayList<PropositionParameterBo>) newParms).add(p); 464 } 465 466 newProp.setParameters(newParms); 467 468 // compoundComponents 469 List<PropositionBo> newCompoundComponents = new ArrayList<PropositionBo>(); 470 for (PropositionBo component : existing.getCompoundComponents()) { 471 PropositionBo newComponent = copyProposition(component); 472 ((ArrayList<PropositionBo>) newCompoundComponents).add(newComponent); 473 } 474 475 newProp.setCompoundComponents(newCompoundComponents); 476 477 return newProp; 478 } 479 480 public String getTermSpecId() { 481 return termSpecId; 482 } 483 484 public void setTermSpecId(String componentId) { 485 this.termSpecId = componentId; 486 } 487 488 public boolean isShowCustomValue() { 489 return showCustomValue; 490 } 491 492 public void setShowCustomValue(boolean showCustomValue) { 493 this.showCustomValue = showCustomValue; 494 } 495 496 public String getTermParameter() { 497 return termParameter; 498 } 499 500 public void setTermParameter(String termParameter) { 501 this.termParameter = termParameter; 502 } 503 504 public List<TermParameter> getTermParameterList() { 505 return termParameterList; 506 } 507 508 public void setTermParameterList(List<TermParameter> termParameterList) { 509 this.termParameterList = termParameterList; 510 } 511 512 @Override 513 public String getId() { 514 return id; 515 } 516 517 public void setId(String id) { 518 this.id = id; 519 } 520 521 @Override 522 public String getDescription() { 523 return description; 524 } 525 526 public void setDescription(String description) { 527 this.description = description; 528 } 529 530 @Override 531 public String getRuleId() { 532 return ruleId; 533 } 534 535 /** 536 * Sets the ruleId on this proposition and all its compound components. 537 * 538 * @param ruleId the ruleId to set 539 */ 540 public void setRuleId(String ruleId) { 541 this.ruleId = ruleId; 542 543 if (getCompoundComponents() != null) for (PropositionBo child : getCompoundComponents()) { 544 child.setRuleId(ruleId); 545 } 546 } 547 548 @Override 549 public String getTypeId() { 550 return typeId; 551 } 552 553 @Override 554 public String getPropositionTypeCode() { 555 return propositionTypeCode; 556 } 557 558 public void setPropositionTypeCode(String propositionTypeCode) { 559 this.propositionTypeCode = propositionTypeCode; 560 } 561 562 @Override 563 public List<PropositionParameterBo> getParameters() { 564 return parameters; 565 } 566 567 public void setParameters(List<PropositionParameterBo> parameters) { 568 this.parameters = parameters; 569 } 570 571 @Override 572 public String getCompoundOpCode() { 573 return compoundOpCode; 574 } 575 576 public void setCompoundOpCode(String compoundOpCode) { 577 this.compoundOpCode = compoundOpCode; 578 } 579 580 @Override 581 public Integer getCompoundSequenceNumber() { 582 return compoundSequenceNumber; 583 } 584 585 public void setCompoundSequenceNumber(Integer compoundSequenceNumber) { 586 this.compoundSequenceNumber = compoundSequenceNumber; 587 } 588 589 @Override 590 public List<PropositionBo> getCompoundComponents() { 591 return compoundComponents; 592 } 593 594 public void setCompoundComponents(List<PropositionBo> compoundComponents) { 595 this.compoundComponents = compoundComponents; 596 } 597 598 public boolean getShowCustomValue() { 599 return showCustomValue; 600 } 601 602 public String getNewTermDescription() { 603 return newTermDescription; 604 } 605 606 public void setNewTermDescription(String newTermDescription) { 607 this.newTermDescription = newTermDescription; 608 } 609}