View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krms.impl.repository;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.eclipse.persistence.annotations.PrivateOwned;
20  import org.kuali.rice.core.api.mo.common.Versioned;
21  import org.kuali.rice.krad.data.DataObjectService;
22  import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
23  import org.kuali.rice.krad.service.KRADServiceLocator;
24  import org.kuali.rice.krad.uif.util.ScriptUtils;
25  import org.kuali.rice.krms.api.repository.LogicalOperator;
26  import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
27  import org.kuali.rice.krms.api.repository.proposition.PropositionDefinitionContract;
28  import org.kuali.rice.krms.api.repository.proposition.PropositionParameter;
29  import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType;
30  import org.kuali.rice.krms.api.repository.proposition.PropositionType;
31  import org.kuali.rice.krms.impl.ui.CustomOperatorUiTranslator;
32  import org.kuali.rice.krms.impl.ui.TermParameter;
33  import org.kuali.rice.krms.impl.util.KrmsImplConstants;
34  import org.kuali.rice.krms.impl.util.KrmsServiceLocatorInternal;
35  
36  import javax.persistence.CascadeType;
37  import javax.persistence.Column;
38  import javax.persistence.Entity;
39  import javax.persistence.GeneratedValue;
40  import javax.persistence.Id;
41  import javax.persistence.JoinColumn;
42  import javax.persistence.JoinTable;
43  import javax.persistence.OneToMany;
44  import javax.persistence.OrderBy;
45  import javax.persistence.Table;
46  import javax.persistence.Transient;
47  import javax.persistence.Version;
48  import java.io.Serializable;
49  import java.util.ArrayList;
50  import java.util.HashMap;
51  import java.util.List;
52  import java.util.Map;
53  import java.util.UUID;
54  
55  @Entity
56  @Table(name = "KRMS_PROP_T")
57  public class PropositionBo implements PropositionDefinitionContract, Versioned, Serializable {
58  
59      private static final long serialVersionUID = 1l;
60  
61      private static final String PROP_SEQ_NAME = "KRMS_PROP_S";
62      static final RepositoryBoIncrementer propositionIdIncrementer = new RepositoryBoIncrementer(PROP_SEQ_NAME);
63      static final RepositoryBoIncrementer propositionParameterIdIncrementer = new RepositoryBoIncrementer("KRMS_PROP_PARM_S");
64  
65      @PortableSequenceGenerator(name = PROP_SEQ_NAME)
66      @GeneratedValue(generator = PROP_SEQ_NAME)
67      @Id
68      @Column(name = "PROP_ID")
69      private String id;
70  
71      @Column(name = "DESC_TXT")
72      private String description;
73  
74      @Column(name = "RULE_ID")
75      private String ruleId;
76  
77      @Column(name = "TYP_ID")
78      private String typeId;
79  
80      @Column(name = "DSCRM_TYP_CD")
81      private String propositionTypeCode;
82  
83      @PrivateOwned
84      @OneToMany(orphanRemoval = true, targetEntity = PropositionParameterBo.class, mappedBy = "proposition",
85              cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE })
86      @OrderBy("sequenceNumber")
87      private List<PropositionParameterBo> parameters = new ArrayList<PropositionParameterBo>();
88  
89      @Column(name = "CMPND_OP_CD")
90      private String compoundOpCode;
91  
92      @Column(name = "CMPND_SEQ_NO")
93      private Integer compoundSequenceNumber;
94  
95      @Column(name = "VER_NBR")
96      @Version
97      private Long versionNumber;
98  
99      @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 }