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 java.io.Serializable;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.Map;
022
023import javax.persistence.CascadeType;
024import javax.persistence.Column;
025import javax.persistence.Entity;
026import javax.persistence.FetchType;
027import javax.persistence.GeneratedValue;
028import javax.persistence.Id;
029import javax.persistence.JoinColumn;
030import javax.persistence.ManyToOne;
031import javax.persistence.Table;
032import javax.persistence.Transient;
033import javax.persistence.Version;
034
035import org.apache.commons.collections.CollectionUtils;
036import org.apache.commons.lang.StringUtils;
037import org.eclipse.persistence.annotations.OptimisticLocking;
038import org.kuali.rice.core.api.mo.common.Versioned;
039import org.kuali.rice.krad.data.CopyOption;
040import org.kuali.rice.krad.data.DataObjectService;
041import org.kuali.rice.krad.data.KradDataServiceLocator;
042import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
043import org.kuali.rice.krms.api.repository.agenda.AgendaDefinitionContract;
044import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
045import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinitionContract;
046import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
047import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
048
049/**
050 * Agenda Item business object
051 *
052 * @author Kuali Rice Team (rice.collab@kuali.org)
053 *
054 */
055@Entity
056@Table(name = "KRMS_AGENDA_ITM_T")
057@OptimisticLocking(cascade = true)
058public class AgendaItemBo implements AgendaItemDefinitionContract, Versioned, Serializable {
059
060    private static final long serialVersionUID = 1L;
061
062    public static final String COPY_OF_TEXT = "Copy of ";
063
064    public static final String AGENDA_ITEM_SEQ_NAME = "KRMS_AGENDA_ITM_S";
065
066    static RepositoryBoIncrementer agendaItemIdIncrementer = new RepositoryBoIncrementer(AGENDA_ITEM_SEQ_NAME);
067
068    @Transient
069    private transient DataObjectService dataObjectService = null;
070    @Transient
071    private transient KrmsTypeRepositoryService krmsTypeRepositoryService = null;
072
073    @PortableSequenceGenerator(name = AGENDA_ITEM_SEQ_NAME)
074    @GeneratedValue(generator = AGENDA_ITEM_SEQ_NAME)
075    @Id
076    @Column(name = "AGENDA_ITM_ID")
077    private String id;
078
079    @Column(name = "AGENDA_ID")
080    private String agendaId;
081
082    @Column(name = "SUB_AGENDA_ID")
083    private String subAgendaId;
084
085    @Column(name = "WHEN_TRUE", insertable = false, updatable = false)
086    private String whenTrueId;
087
088    @Column(name = "WHEN_FALSE", insertable = false, updatable = false)
089    private String whenFalseId;
090
091    @Column(name = "ALWAYS", insertable = false, updatable = false)
092    private String alwaysId;
093
094    @ManyToOne(targetEntity = RuleBo.class, fetch = FetchType.LAZY, cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.REMOVE, CascadeType.PERSIST})
095    @JoinColumn(name = "RULE_ID", referencedColumnName = "RULE_ID")
096    private RuleBo rule;
097
098    @Column(name = "VER_NBR")
099    @Version
100    private Long versionNumber;
101
102    @ManyToOne(targetEntity = AgendaItemBo.class, cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE })
103    @JoinColumn(name = "WHEN_TRUE", referencedColumnName = "AGENDA_ITM_ID")
104    private AgendaItemBo whenTrue;
105
106    @ManyToOne(targetEntity = AgendaItemBo.class, cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE })
107    @JoinColumn(name = "WHEN_FALSE", referencedColumnName = "AGENDA_ITM_ID")
108    private AgendaItemBo whenFalse;
109
110    @ManyToOne(targetEntity = AgendaItemBo.class, cascade = { CascadeType.REFRESH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE })
111    @JoinColumn(name = "ALWAYS", referencedColumnName = "AGENDA_ITM_ID")
112    private AgendaItemBo always;
113
114    public String getUl(AgendaItemBo firstItem) {
115        return ("<ul>" + getUlHelper(firstItem) + "</ul>");
116    }
117
118    public String getUlHelper(AgendaItemBo item) {
119        StringBuilder sb = new StringBuilder();
120        sb.append("<li>" + getRuleId() + "</li>");
121
122        if (whenTrue != null) {
123            sb.append("<ul><li>when true</li><ul>");
124            sb.append(getUlHelper(whenTrue));
125            sb.append("</ul></ul>");
126        }
127        if (whenFalse != null) {
128            sb.append("<ul><li>when false</li><ul>");
129            sb.append(getUlHelper(whenFalse));
130            sb.append("</ul></ul>");
131        }
132        if (always != null) {
133            sb.append(getUlHelper(always));
134        }
135
136        return sb.toString();
137    }
138
139    public String getRuleText() {
140        StringBuilder resultBuilder = new StringBuilder();
141        if (getRule() != null) {
142            if (StringUtils.isBlank(getRule().getName())) {
143                resultBuilder.append("- unnamed rule -");
144            } else {
145                resultBuilder.append(getRule().getName());
146            }
147            if (!StringUtils.isBlank(getRule().getDescription())) {
148                resultBuilder.append(": ");
149                resultBuilder.append(getRule().getDescription());
150            }
151
152            // add a description of the action configured on the rule, if there is one
153            if (!CollectionUtils.isEmpty(getRule().getActions())) {
154                resultBuilder.append("   [");
155                ActionBo action = getRule().getActions().get(0);
156                KrmsTypeDefinition krmsTypeDefn = getKrmsTypeRepositoryService().getTypeById(action.getTypeId());
157                resultBuilder.append(krmsTypeDefn.getName());
158                resultBuilder.append(": ");
159                resultBuilder.append(action.getName());
160
161                if (getRule().getActions().size() > 1) {
162                    resultBuilder.append(" ... ");
163                }
164
165                resultBuilder.append("]");
166            }
167        } else {
168            throw new IllegalStateException();
169        }
170
171        return resultBuilder.toString();
172    }
173
174    public List<AgendaItemBo> getAlwaysList() {
175        List<AgendaItemBo> results = new ArrayList<AgendaItemBo>();
176        AgendaItemBo currentNode = this;
177
178        while (currentNode.always != null) {
179            results.add(currentNode.always);
180            currentNode = currentNode.always;
181        }
182
183        return results;
184    }
185
186    /**
187     * @return the id
188     */
189    @Override
190    public String getId() {
191        return this.id;
192    }
193
194    /**
195     * @param id the id to set
196     */
197    public void setId(String id) {
198        this.id = id;
199    }
200
201    /**
202     * @return the agendaId
203     */
204    @Override
205    public String getAgendaId() {
206        return this.agendaId;
207    }
208
209    /**
210     * @param agendaId the agendaId to set
211     */
212    public void setAgendaId(String agendaId) {
213        this.agendaId = agendaId;
214    }
215
216    /**
217     * @return the ruleId
218     */
219    @Override
220    public String getRuleId() {
221        if (rule != null) {
222            return rule.getId();
223        }
224
225        return null;
226    }
227
228    /**
229     * @return the subAgendaId
230     */
231    @Override
232    public String getSubAgendaId() {
233        return this.subAgendaId;
234    }
235
236    /**
237     * @param subAgendaId the subAgendaId to set
238     */
239    public void setSubAgendaId(String subAgendaId) {
240        this.subAgendaId = subAgendaId;
241    }
242
243    /**
244     * @return the whenTrueId
245     */
246    @Override
247    public String getWhenTrueId() {
248        return this.whenTrueId;
249    }
250
251    /**
252     * @param whenTrueId the whenTrueId to set
253     */
254    public void setWhenTrueId(String whenTrueId) {
255        this.whenTrueId = whenTrueId;
256    }
257
258    /**
259     * @return the whenFalseId
260     */
261    @Override
262    public String getWhenFalseId() {
263        return this.whenFalseId;
264    }
265
266    /**
267     * @param whenFalseId the whenFalseId to set
268     */
269    public void setWhenFalseId(String whenFalseId) {
270        this.whenFalseId = whenFalseId;
271    }
272
273    /**
274     * @return the alwaysId
275     */
276    @Override
277    public String getAlwaysId() {
278        return this.alwaysId;
279    }
280
281    /**
282     * @param alwaysId the alwaysId to set
283     */
284    public void setAlwaysId(String alwaysId) {
285        this.alwaysId = alwaysId;
286    }
287
288    @Override
289    public Long getVersionNumber() {
290        return versionNumber;
291    }
292
293    public void setVersionNumber(Long versionNumber) {
294        this.versionNumber = versionNumber;
295    }
296
297    /**
298     * @return the whenTrue
299     */
300    @Override
301    public AgendaItemBo getWhenTrue() {
302        return this.whenTrue;
303    }
304
305    /**
306     * @param whenTrue the whenTrue to set
307     */
308    public void setWhenTrue(AgendaItemBo whenTrue) {
309        this.whenTrue = whenTrue;
310
311        if (whenTrue != null) {
312            setWhenTrueId(whenTrue.getId());
313        } else {
314            setWhenTrueId(null);
315        }
316    }
317
318    /**
319     * @return the whenFalse
320     */
321    @Override
322    public AgendaItemBo getWhenFalse() {
323        return this.whenFalse;
324    }
325
326    /**
327     * @param whenFalse the whenFalse to set
328     */
329    public void setWhenFalse(AgendaItemBo whenFalse) {
330        this.whenFalse = whenFalse;
331
332        if (whenFalse != null) {
333            setWhenFalseId(whenFalse.getId());
334        } else {
335            setWhenFalseId(null);
336        }
337    }
338
339    /**
340     * @return the always
341     */
342    @Override
343    public AgendaItemBo getAlways() {
344        return this.always;
345    }
346
347    /**
348     * @param always the always to set
349     */
350    public void setAlways(AgendaItemBo always) {
351        this.always = always;
352        if (always != null) {
353            setAlwaysId(always.getId());
354        } else {
355            setAlwaysId(null);
356        }
357    }
358
359    /**
360     * @return the rule
361     */
362    @Override
363    public RuleBo getRule() {
364        return this.rule;
365    }
366
367    @Override
368    public AgendaDefinitionContract getSubAgenda() {
369        return null; // no sub-agenda support at this time
370    }
371
372    /**
373     * @param rule the rule to set
374     */
375    public void setRule(RuleBo rule) {
376        this.rule = rule;
377    }
378
379    /**
380     * Converts a mutable bo to it's immutable counterpart
381     * @param bo the mutable business object
382     * @return the immutable object
383     */
384    static AgendaItemDefinition to(AgendaItemBo bo) {
385        if (bo == null) {
386            return null;
387        }
388
389        AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(bo);
390
391        return builder.build();
392    }
393
394    /**
395     * Converts a immutable object to it's mutable bo counterpart
396     * @param im immutable object
397     * @return the mutable bo
398     */
399    public static AgendaItemBo from(AgendaItemDefinition im) {
400        if (im == null) {
401            return null;
402        }
403
404        AgendaItemBo bo = new AgendaItemBo();
405        bo.id = im.getId();
406        bo.agendaId = im.getAgendaId();
407        bo.subAgendaId = im.getSubAgendaId();
408        bo.whenTrueId = im.getWhenTrueId();
409        bo.whenFalseId = im.getWhenFalseId();
410        bo.alwaysId = im.getAlwaysId();
411        bo.versionNumber = im.getVersionNumber();
412        bo.rule = RuleBo.from(im.getRule());
413        bo.whenTrue = AgendaItemBo.from(im.getWhenTrue());
414        bo.whenFalse = AgendaItemBo.from(im.getWhenFalse());
415        bo.always = AgendaItemBo.from(im.getAlways());
416
417        return bo;
418    }
419
420    /**
421     * Returns a copy of this AgendaItem
422     * @param copiedAgenda the new Agenda that the copied AgendiaItem will be associated with
423     * @param oldRuleIdToNew Map<String, RuleBo> mapping of old rule id to the new RuleBo
424     * @param dts DateTimeStamp to append to the copied AgendaItem name
425     * @return AgendaItemBo copy of this AgendaItem with new id and name
426     */
427    public AgendaItemBo copyAgendaItem(AgendaBo copiedAgenda, Map<String, RuleBo> oldRuleIdToNew, Map<String, AgendaItemBo> oldAgendaItemIdToNew, List<AgendaItemBo> copiedAgendaItems, final String dts) {
428        // Use deepCopy and update all the ids.
429        AgendaItemBo copiedAgendaItem = getDataObjectService().copyInstance(this, CopyOption.RESET_PK_FIELDS, CopyOption.RESET_OBJECT_ID );
430        copiedAgendaItem.setId(agendaItemIdIncrementer.getNewId());
431        copiedAgendaItem.setAgendaId(copiedAgenda.getId());
432        oldAgendaItemIdToNew.put(this.getId(), copiedAgendaItem);
433
434        // Don't create another copy of a rule that we have already copied.
435        if (!oldRuleIdToNew.containsKey(this.getRuleId())) {
436            if (this.getRule() != null) {
437                copiedAgendaItem.setRule(this.getRule().copyRule(COPY_OF_TEXT + this.getRule().getName() + " " + dts));
438                oldRuleIdToNew.put(this.getRuleId(), copiedAgendaItem.getRule());
439            }
440        } else {
441            copiedAgendaItem.setRule(oldRuleIdToNew.get(this.getRuleId()));
442        }
443
444        if (copiedAgendaItem.getWhenFalse() != null) {
445            if (!oldAgendaItemIdToNew.containsKey(this.getWhenFalseId())) {
446                copiedAgendaItem.setWhenFalse(this.getWhenFalse().copyAgendaItem(copiedAgenda, oldRuleIdToNew, oldAgendaItemIdToNew, copiedAgendaItems, dts));
447                oldAgendaItemIdToNew.put(this.getWhenFalseId(), copiedAgendaItem.getWhenFalse());
448                copiedAgendaItems.add(copiedAgendaItem.getWhenFalse());
449            } else {
450                copiedAgendaItem.setWhenFalse(oldAgendaItemIdToNew.get(this.getWhenFalseId()));
451            }
452        }
453
454        if (copiedAgendaItem.getWhenTrue() != null) {
455            if (!oldAgendaItemIdToNew.containsKey(this.getWhenTrueId())) {
456                copiedAgendaItem.setWhenTrue(this.getWhenTrue().copyAgendaItem(copiedAgenda, oldRuleIdToNew, oldAgendaItemIdToNew, copiedAgendaItems, dts));
457                oldAgendaItemIdToNew.put(this.getWhenTrueId(), copiedAgendaItem.getWhenTrue());
458                copiedAgendaItems.add(copiedAgendaItem.getWhenTrue());
459            } else {
460                copiedAgendaItem.setWhenTrue(oldAgendaItemIdToNew.get(this.getWhenTrueId()));
461            }
462        }
463
464        if (copiedAgendaItem.getAlways() != null) {
465            if (!oldAgendaItemIdToNew.containsKey(this.getAlwaysId())) {
466                copiedAgendaItem.setAlways(this.getAlways().copyAgendaItem(copiedAgenda, oldRuleIdToNew, oldAgendaItemIdToNew, copiedAgendaItems, dts));
467                oldAgendaItemIdToNew.put(this.getAlwaysId(), copiedAgendaItem.getAlways());
468                copiedAgendaItems.add(copiedAgendaItem.getAlways());
469            } else {
470                copiedAgendaItem.setAlways(oldAgendaItemIdToNew.get(this.getAlwaysId()));
471            }
472        }
473        return copiedAgendaItem;
474    }
475
476    public DataObjectService getDataObjectService() {
477        if (dataObjectService == null) {
478            dataObjectService = KradDataServiceLocator.getDataObjectService();
479        }
480
481        return dataObjectService;
482    }
483
484    public void setDataObjectService(DataObjectService dataObjectService) {
485        this.dataObjectService = dataObjectService;
486    }
487
488    public KrmsTypeRepositoryService getKrmsTypeRepositoryService() {
489        if (krmsTypeRepositoryService == null) {
490            krmsTypeRepositoryService = KrmsRepositoryServiceLocator.getKrmsTypeRepositoryService();
491        }
492
493        return krmsTypeRepositoryService;
494    }
495
496    public void setKrmsTypeRepositoryService(KrmsTypeRepositoryService dataObjectService) {
497        this.krmsTypeRepositoryService = dataObjectService;
498    }
499}