View Javadoc
1   /**
2    * Copyright 2005-2014 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.kuali.rice.core.api.mo.common.Versioned;
20  import org.kuali.rice.krad.data.DataObjectService;
21  import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
22  import org.kuali.rice.krad.service.KRADServiceLocator;
23  import org.kuali.rice.krms.api.repository.LogicalOperator;
24  import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
25  import org.kuali.rice.krms.api.repository.proposition.PropositionDefinitionContract;
26  import org.kuali.rice.krms.api.repository.proposition.PropositionParameter;
27  import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType;
28  import org.kuali.rice.krms.api.repository.proposition.PropositionType;
29  import org.kuali.rice.krms.impl.ui.CustomOperatorUiTranslator;
30  import org.kuali.rice.krms.impl.ui.TermParameter;
31  import org.kuali.rice.krms.impl.util.KrmsImplConstants;
32  import org.kuali.rice.krms.impl.util.KrmsServiceLocatorInternal;
33  
34  import javax.persistence.CascadeType;
35  import javax.persistence.Column;
36  import javax.persistence.Entity;
37  import javax.persistence.GeneratedValue;
38  import javax.persistence.Id;
39  import javax.persistence.JoinColumn;
40  import javax.persistence.JoinTable;
41  import javax.persistence.ManyToMany;
42  import javax.persistence.OneToMany;
43  import javax.persistence.OrderBy;
44  import javax.persistence.Table;
45  import javax.persistence.Transient;
46  import javax.persistence.Version;
47  import java.io.IOException;
48  import java.io.ObjectOutputStream;
49  import java.io.Serializable;
50  import java.util.ArrayList;
51  import java.util.Arrays;
52  import java.util.HashMap;
53  import java.util.List;
54  import java.util.Map;
55  import java.util.UUID;
56  
57  @Entity
58  @Table(name = "KRMS_PROP_T")
59  public class PropositionBo implements PropositionDefinitionContract, Versioned, Serializable {
60  
61      private static final long serialVersionUID = 1l;
62  
63      private static final String PROP_SEQ_NAME = "KRMS_PROP_S";
64      static final RepositoryBoIncrementer propositionIdIncrementer = new RepositoryBoIncrementer(PROP_SEQ_NAME);
65      static final RepositoryBoIncrementer propositionParameterIdIncrementer = new RepositoryBoIncrementer("KRMS_PROP_PARM_S");
66  
67      @PortableSequenceGenerator(name = PROP_SEQ_NAME)
68      @GeneratedValue(generator = PROP_SEQ_NAME)
69      @Id
70      @Column(name = "PROP_ID")
71      private String id;
72  
73      @Column(name = "DESC_TXT")
74      private String description;
75  
76      @Column(name = "RULE_ID")
77      private String ruleId;
78  
79      @Column(name = "TYP_ID")
80      private String typeId;
81  
82      @Column(name = "DSCRM_TYP_CD")
83      private String propositionTypeCode;
84  
85      @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "proposition")
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      @ManyToMany(targetEntity = PropositionBo.class, cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.REMOVE, CascadeType.PERSIST })
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                 sb.append(getParamValue(parameters.get(0))).append(" ").append(getParamValue(parameters.get(2)));
141 
142                 if (valueDisplay != null) {
143                     // !=null and =null operators values will be null and should not be displayed  
144                     sb.append(" ").append(valueDisplay);
145                 }
146 
147                 setParameterDisplayString(sb.toString());
148             } else {
149                 // should not happen
150             }
151         }
152     }
153 
154     /**
155      * returns the string summary value for the given proposition parameter.
156      *
157      * @param param the proposition parameter to get the summary value for
158      * @return the summary value
159      */
160     private String getParamValue(PropositionParameterBo param) {
161         CustomOperatorUiTranslator customOperatorUiTranslator =
162                 KrmsServiceLocatorInternal.getCustomOperatorUiTranslator();
163 
164         if (PropositionParameterType.TERM.getCode().equalsIgnoreCase(param.getParameterType())) {
165             String termName = "";
166             String termId = param.getValue();
167 
168             if (termId != null && termId.length() > 0) {
169                 if (termId.startsWith(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) {
170                     if (!StringUtils.isBlank(newTermDescription)) {
171                         termName = newTermDescription;
172                     } else {
173                         TermSpecificationBo termSpec = getDataObjectService().find(TermSpecificationBo.class,
174                                 termId.substring(1 + termId.indexOf(":")));
175                         termName = termSpec.getName() + "(...)";
176                     }
177                 } else {
178                     TermBo term = getDataObjectService().find(TermBo.class, termId);
179                     termName = term.getSpecification().getName();
180                 }
181             }
182 
183             return termName;
184 
185         } else if (PropositionParameterType.FUNCTION.getCode().equalsIgnoreCase(param.getParameterType()) ||
186                 PropositionParameterType.OPERATOR.getCode().equalsIgnoreCase(param.getParameterType())) {
187             if (customOperatorUiTranslator.isCustomOperatorFormValue(param.getValue())) {
188                 String functionName = customOperatorUiTranslator.getCustomOperatorName(param.getValue());
189                 if (!StringUtils.isEmpty(functionName)) {
190                     return functionName;
191                 }
192             }
193         }
194 
195         return param.getValue();
196     }
197 
198     /**
199      * @return the parameterDisplayString
200      */
201     public String getParameterDisplayString() {
202         setupParameterDisplayString();
203 
204         return this.parameterDisplayString;
205     }
206 
207     /**
208      * @param parameterDisplayString the parameterDisplayString to set
209      */
210     public void setParameterDisplayString(String parameterDisplayString) {
211         this.parameterDisplayString = parameterDisplayString;
212     }
213 
214     public boolean getEditMode() {
215         return this.editMode;
216     }
217 
218     public void setEditMode(boolean editMode) {
219         this.editMode = editMode;
220     }
221 
222     public String getCategoryId() {
223         return this.categoryId;
224     }
225 
226     public void setCategoryId(String categoryId) {
227         this.categoryId = categoryId;
228     }
229 
230     /**
231      * set the typeId.  If the parameter is blank, then this PropositionBo's
232      * typeId will be set to null
233      *
234      * @param typeId
235      */
236     public void setTypeId(String typeId) {
237         if (StringUtils.isBlank(typeId)) {
238             this.typeId = null;
239         } else {
240             this.typeId = typeId;
241         }
242     }
243 
244     public Long getVersionNumber() {
245         return versionNumber;
246     }
247 
248     public void setVersionNumber(Long versionNumber) {
249         this.versionNumber = versionNumber;
250     }
251 
252     public Map<String, String> getTermParameters() {
253         return termParameters;
254     }
255 
256     public void setTermParameters(Map<String, String> termParameters) {
257         this.termParameters = termParameters;
258     }
259 
260     public DataObjectService getDataObjectService() {
261         return KRADServiceLocator.getDataObjectService();
262     }
263 
264     /**
265      * Converts a mutable bo to it's immutable counterpart
266      *
267      * @param bo the mutable business object
268      * @return the immutable object
269      */
270     public static PropositionDefinition to(PropositionBo bo) {
271         if (bo == null) {
272             return null;
273         }
274 
275         return PropositionDefinition.Builder.create(bo).build();
276     }
277 
278     /**
279      * Converts a immutable object to it's mutable bo counterpart
280      *
281      * @param im immutable object
282      * @return the mutable bo
283      */
284     public static PropositionBo from(PropositionDefinition im) {
285         if (im == null) {
286             return null;
287         }
288 
289         PropositionBo bo = new PropositionBo();
290         bo.id = im.getId();
291         bo.description = im.getDescription();
292 
293         // we don't set rule here, it is set in RuleBo.from
294 
295         setRuleIdRecursive(im.getRuleId(), bo);
296 
297         bo.typeId = im.getTypeId();
298         bo.propositionTypeCode = im.getPropositionTypeCode();
299         bo.parameters = new ArrayList<PropositionParameterBo>();
300 
301         for (PropositionParameter parm : im.getParameters()) {
302             PropositionParameterBo parmBo = PropositionParameterBo.from(parm);
303             bo.parameters.add(parmBo);
304             parmBo.setProposition(bo);
305         }
306 
307         bo.compoundOpCode = im.getCompoundOpCode();
308         bo.compoundSequenceNumber = im.getCompoundSequenceNumber();
309         bo.compoundComponents = new ArrayList<PropositionBo>();
310 
311         if (im.getCompoundComponents() != null) for (PropositionDefinition prop : im.getCompoundComponents()) {
312             bo.compoundComponents.add(PropositionBo.from(prop));
313         }
314 
315         bo.setVersionNumber(im.getVersionNumber());
316 
317         return bo;
318     }
319 
320     private static void setRuleIdRecursive(String ruleId, PropositionBo prop) {
321         prop.ruleId = ruleId;
322 
323         if (prop.compoundComponents != null) {
324             for (PropositionBo child : prop.compoundComponents) {
325                 if (child != null) {
326                     setRuleIdRecursive(ruleId, child);
327                 }
328             }
329         }
330     }
331 
332     /**
333      * This method creates a partially populated Simple PropositionBo with
334      * three parameters:  a term type paramter (value not assigned)
335      * a operation parameter
336      * a constant parameter (value set to empty string)
337      * The returned PropositionBo has an generatedId. The type code and ruleId properties are assigned the
338      * same value as the sibling param passed in.
339      * Each PropositionParameter has the id generated, and type, sequenceNumber,
340      * propId default values set. The value is set to "".
341      *
342      * @param sibling -
343      * @param pType
344      * @return a PropositionBo partially populated.
345      */
346     public static PropositionBo createSimplePropositionBoStub(PropositionBo sibling, String pType) {
347         // create a simple proposition Bo  
348         PropositionBo prop = null;
349         if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(pType)) {
350             prop = new PropositionBo();
351             prop.setId(propositionIdIncrementer.getNewId());
352             prop.setPropositionTypeCode(pType);
353             prop.setEditMode(true);
354 
355             if (sibling != null) {
356                 prop.setRuleId(sibling.getRuleId());
357             }
358 
359             // create blank proposition parameters  
360             PropositionParameterBo pTerm = new PropositionParameterBo();
361             pTerm.setId(propositionParameterIdIncrementer.getNewId());
362             pTerm.setParameterType("T");
363             pTerm.setProposition(prop);
364             pTerm.setSequenceNumber(new Integer("0"));
365             pTerm.setVersionNumber(new Long(1));
366             pTerm.setValue("");
367 
368             // create blank proposition parameters  
369             PropositionParameterBo pOp = new PropositionParameterBo();
370             pOp.setId(propositionParameterIdIncrementer.getNewId());
371             pOp.setParameterType("O");
372             pOp.setProposition(prop);
373             pOp.setSequenceNumber(new Integer("2"));
374             pOp.setVersionNumber(new Long(1));
375 
376             // create blank proposition parameters  
377             PropositionParameterBo pConst = new PropositionParameterBo();
378             pConst.setId(propositionParameterIdIncrementer.getNewId());
379             pConst.setParameterType("C");
380             pConst.setProposition(prop);
381             pConst.setSequenceNumber(new Integer("1"));
382             pConst.setVersionNumber(new Long(1));
383             pConst.setValue("");
384             List<PropositionParameterBo> paramList = Arrays.asList(pTerm, pConst, pOp);
385 
386             prop.setParameters(paramList);
387         }
388 
389         return prop;
390     }
391 
392     public static PropositionBo createCompoundPropositionBoStub(PropositionBo existing, boolean addNewChild) {
393         // create a simple proposition Bo  
394         PropositionBo prop = new PropositionBo();
395         prop.setId(propositionIdIncrementer.getNewId());
396         prop.setPropositionTypeCode(PropositionType.COMPOUND.getCode());
397         prop.setCompoundOpCode(LogicalOperator.AND.getCode());
398 
399         // default to and  
400         prop.setDescription("");
401         prop.setEditMode(true);
402 
403         if (existing != null) {
404             prop.setRuleId(existing.getRuleId());
405         }
406 
407         List<PropositionBo> components = new ArrayList<PropositionBo>(2);
408         components.add(existing);
409 
410         if (addNewChild) {
411             PropositionBo newProp = createSimplePropositionBoStub(existing, PropositionType.SIMPLE.getCode());
412             components.add(newProp);
413             prop.setEditMode(false);
414         }
415 
416         prop.setCompoundComponents(components);
417 
418         return prop;
419     }
420 
421     public static PropositionBo createCompoundPropositionBoStub2(PropositionBo existing) {
422         // create a simple proposition Bo  
423         PropositionBo prop = new PropositionBo();
424         prop.setId(propositionIdIncrementer.getNewId());
425         prop.setPropositionTypeCode(PropositionType.COMPOUND.getCode());
426         prop.setRuleId(existing.getRuleId());
427         prop.setCompoundOpCode(LogicalOperator.AND.getCode());
428         // default to and  
429         prop.setDescription("");
430         prop.setEditMode(true);
431         List<PropositionBo> components = new ArrayList<PropositionBo>();
432         ((ArrayList<PropositionBo>) components).add(existing);
433         prop.setCompoundComponents(components);
434 
435         return prop;
436     }
437 
438     public static PropositionBo copyProposition(PropositionBo existing) {
439         // Note: RuleId is not set  
440         PropositionBo newProp = new PropositionBo();
441         newProp.setId(propositionIdIncrementer.getNewId());
442         newProp.setDescription(existing.getDescription());
443         newProp.setPropositionTypeCode(existing.getPropositionTypeCode());
444         newProp.setTypeId(existing.getTypeId());
445         newProp.setCompoundOpCode(existing.getCompoundOpCode());
446         newProp.setCompoundSequenceNumber(existing.getCompoundSequenceNumber());
447 
448         // parameters  
449         List<PropositionParameterBo> newParms = new ArrayList<PropositionParameterBo>();
450 
451         for (PropositionParameterBo parm : existing.getParameters()) {
452             PropositionParameterBo p = new PropositionParameterBo();
453             p.setId(propositionParameterIdIncrementer.getNewId());
454             p.setParameterType(parm.getParameterType());
455             p.setProposition(newProp);
456             p.setSequenceNumber(parm.getSequenceNumber());
457             p.setValue(parm.getValue());
458             ((ArrayList<PropositionParameterBo>) newParms).add(p);
459         }
460 
461         newProp.setParameters(newParms);
462 
463         // compoundComponents
464         List<PropositionBo> newCompoundComponents = new ArrayList<PropositionBo>();
465         for (PropositionBo component : existing.getCompoundComponents()) {
466             PropositionBo newComponent = copyProposition(component);
467             ((ArrayList<PropositionBo>) newCompoundComponents).add(component);
468         }
469 
470         newProp.setCompoundComponents(newCompoundComponents);
471 
472         return newProp;
473     }
474 
475     /*
476      * This is being done because there is a  major issue with lazy relationships, in ensuring that the relationship is
477      * still available after the object has been detached, or serialized. For most JPA providers, after serialization
478      * any lazy relationship that was not instantiated will be broken, and either throw an error when accessed,
479      * or return null.
480      */
481     private void writeObject(ObjectOutputStream stream) throws IOException, ClassNotFoundException {
482         parameters.size();
483         stream.defaultWriteObject();
484     }
485 
486     public String getTermSpecId() {
487         return termSpecId;
488     }
489 
490     public void setTermSpecId(String componentId) {
491         this.termSpecId = componentId;
492     }
493 
494     public boolean isShowCustomValue() {
495         return showCustomValue;
496     }
497 
498     public void setShowCustomValue(boolean showCustomValue) {
499         this.showCustomValue = showCustomValue;
500     }
501 
502     public String getTermParameter() {
503         return termParameter;
504     }
505 
506     public void setTermParameter(String termParameter) {
507         this.termParameter = termParameter;
508     }
509 
510     public List<TermParameter> getTermParameterList() {
511         return termParameterList;
512     }
513 
514     public void setTermParameterList(List<TermParameter> termParameterList) {
515         this.termParameterList = termParameterList;
516     }
517 
518     public String getId() {
519         return id;
520     }
521 
522     public void setId(String id) {
523         this.id = id;
524     }
525 
526     public String getDescription() {
527         return description;
528     }
529 
530     public void setDescription(String description) {
531         this.description = description;
532     }
533 
534     public String getRuleId() {
535         return ruleId;
536     }
537 
538     public void setRuleId(String ruleId) {
539         this.ruleId = ruleId;
540     }
541 
542     public String getTypeId() {
543         return typeId;
544     }
545 
546     public String getPropositionTypeCode() {
547         return propositionTypeCode;
548     }
549 
550     public void setPropositionTypeCode(String propositionTypeCode) {
551         this.propositionTypeCode = propositionTypeCode;
552     }
553 
554     public List<PropositionParameterBo> getParameters() {
555         return parameters;
556     }
557 
558     public void setParameters(List<PropositionParameterBo> parameters) {
559         this.parameters = parameters;
560     }
561 
562     public String getCompoundOpCode() {
563         return compoundOpCode;
564     }
565 
566     public void setCompoundOpCode(String compoundOpCode) {
567         this.compoundOpCode = compoundOpCode;
568     }
569 
570     public Integer getCompoundSequenceNumber() {
571         return compoundSequenceNumber;
572     }
573 
574     public void setCompoundSequenceNumber(Integer compoundSequenceNumber) {
575         this.compoundSequenceNumber = compoundSequenceNumber;
576     }
577 
578     public List<PropositionBo> getCompoundComponents() {
579         return compoundComponents;
580     }
581 
582     public void setCompoundComponents(List<PropositionBo> compoundComponents) {
583         this.compoundComponents = compoundComponents;
584     }
585 
586     public boolean getShowCustomValue() {
587         return showCustomValue;
588     }
589 
590     public String getNewTermDescription() {
591         return newTermDescription;
592     }
593 
594     public void setNewTermDescription(String newTermDescription) {
595         this.newTermDescription = newTermDescription;
596     }
597 }