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.kew.impl.peopleflow;
17  
18  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
19  import org.kuali.rice.core.api.mo.common.active.MutableInactivatable;
20  import org.kuali.rice.kew.api.KEWPropertyConstants;
21  import org.kuali.rice.kew.api.KewApiServiceLocator;
22  import org.kuali.rice.kew.api.peopleflow.PeopleFlowContract;
23  import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
24  import org.kuali.rice.kew.api.peopleflow.PeopleFlowMember;
25  import org.kuali.rice.kew.api.repository.type.KewAttributeDefinition;
26  import org.kuali.rice.kew.api.repository.type.KewTypeAttribute;
27  import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
28  import org.kuali.rice.kew.impl.type.KewTypeBo;
29  import org.kuali.rice.krad.data.jpa.converters.BooleanYNConverter;
30  import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
31  import org.kuali.rice.krad.util.BeanPropertyComparator;
32  
33  import javax.persistence.CascadeType;
34  import javax.persistence.Column;
35  import javax.persistence.Convert;
36  import javax.persistence.Entity;
37  import javax.persistence.FetchType;
38  import javax.persistence.GeneratedValue;
39  import javax.persistence.Id;
40  import javax.persistence.JoinColumn;
41  import javax.persistence.ManyToOne;
42  import javax.persistence.OneToMany;
43  import javax.persistence.PostLoad;
44  import javax.persistence.Table;
45  import javax.persistence.Transient;
46  import javax.persistence.Version;
47  import java.io.Serializable;
48  import java.util.ArrayList;
49  import java.util.Collections;
50  import java.util.HashMap;
51  import java.util.HashSet;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.Set;
55  
56  /**
57   *  Mapped entity for PeopleFlows
58   *
59   *  @author Kuali Rice Team (rice.collab@kuali.org)
60   */
61  @Entity
62  @Table(name = "KREW_PPL_FLW_T")
63  public class PeopleFlowBo implements Serializable, PeopleFlowContract, MutableInactivatable {
64  
65      private static final long serialVersionUID = -4911187431645573793L;
66  
67      @Id
68      @GeneratedValue(generator = "KREW_PPL_FLW_S")
69      @PortableSequenceGenerator(name = "KREW_PPL_FLW_S")
70      @Column(name = "PPL_FLW_ID", nullable = false)
71      private String id;
72  
73      @Column(name = "NM", nullable = false)
74      private String name;
75  
76      @Column(name = "NMSPC_CD", nullable = false)
77      private String namespaceCode;
78  
79      @Column(name = "TYP_ID")
80      private String typeId;
81  
82      @Column(name = "DESC_TXT")
83      private String description;
84  
85      @Column(name = "ACTV", nullable = false)
86      @Convert(converter = BooleanYNConverter.class)
87      private boolean active = true;
88  
89      @Version
90      @Column(name = "VER_NBR", nullable = false)
91      private Long versionNumber;
92  
93      @ManyToOne
94      @JoinColumn(name = "TYP_ID", insertable = false, updatable = false)
95      private KewTypeBo typeBo;
96  
97      @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "peopleFlow", orphanRemoval = true)
98      private List<PeopleFlowAttributeBo> attributeBos = new ArrayList<PeopleFlowAttributeBo>();
99  
100     @OneToMany(cascade = CascadeType.ALL, mappedBy = "peopleFlow", orphanRemoval = true)
101     private List<PeopleFlowMemberBo> members = new ArrayList<PeopleFlowMemberBo>();
102 
103     // non-persisted, used for maintenance
104     @Transient
105     private Map<String, String> attributeValues = new HashMap<String, String>();
106 
107     public static PeopleFlowBo from(PeopleFlowDefinition peopleFlow, KewTypeDefinition kewTypeDefinition) {
108         return PeopleFlowBo.fromAndUpdate(peopleFlow, kewTypeDefinition, null);
109     }
110 
111     /**
112      * Translates from the given PeopleFlowDefinition to a PeopleFlowBo, optionally updating the given "toUpdate" parameter
113      * instead of creating a new PeopleFlowBo.  If it's not passed then a new PeopleFlowBo will be created.
114      */
115     public static PeopleFlowBo fromAndUpdate(PeopleFlowDefinition peopleFlow, KewTypeDefinition kewTypeDefinition,
116             PeopleFlowBo toUpdate) {
117 
118         PeopleFlowBo result = toUpdate;
119 
120         if (null == toUpdate) {
121             result = new PeopleFlowBo();
122         }
123 
124         result.setId(peopleFlow.getId());
125         result.setName(peopleFlow.getName());
126         result.setNamespaceCode(peopleFlow.getNamespaceCode());
127         result.setTypeId(peopleFlow.getTypeId());
128         result.setDescription(peopleFlow.getDescription());
129         result.setActive(peopleFlow.isActive());
130         result.setVersionNumber(peopleFlow.getVersionNumber());
131         
132         // we need to translate attributes over, this is a bit more work, first let's do some validation
133         if (null == peopleFlow.getTypeId()) {
134             if (null != kewTypeDefinition) {
135                 throw new RiceIllegalArgumentException("PeopleFlow has no type id, but a KewTypeDefinition was " +
136                         "supplied when it should not have been.");
137             }
138         }
139         if (null != peopleFlow.getTypeId()) {
140             if (kewTypeDefinition == null) {
141                 throw new RiceIllegalArgumentException("PeopleFlow has a type id of '" + peopleFlow.getTypeId() +
142                         "' but no KewTypeDefinition was supplied.");
143             }
144             if (!kewTypeDefinition.getId().equals(peopleFlow.getTypeId())) {
145                 throw new RiceIllegalArgumentException("Type id of given KewTypeDefinition does not match PeopleFlow " +
146                         "type id:  " + kewTypeDefinition.getId() + " != " + peopleFlow.getTypeId());
147             }
148         }
149 
150         // now we need to effectively do a diff with the given attributes, first let's add new entries and update
151         // existing ones
152         // TODO - ensure this is correct
153         ArrayList attributesToAdd = new ArrayList<PeopleFlowAttributeBo>();
154         // if type is null drop attributes
155         if (null != peopleFlow.getTypeId()) {
156             for (String key : peopleFlow.getAttributes().keySet()) {
157                 KewAttributeDefinition attributeDefinition = kewTypeDefinition.getAttributeDefinitionByName(key);
158                 if (null == attributeDefinition) {
159                     throw new RiceIllegalArgumentException("There is no attribute definition for the given attribute " +
160                             "name '" + key + "'");
161                 }
162                 attributesToAdd.add(PeopleFlowAttributeBo.from(attributeDefinition, null, result,
163                         peopleFlow.getAttributes().get(key)));
164             }
165             result.setAttributeBos(attributesToAdd);
166         }
167         // TODO - END
168         handleMembersUpdate(result, peopleFlow);
169 
170         return result;
171     }
172 
173     /**
174      * Translate the members, if the members have changed at all, we want to clear so that the current set of members
175      * are removed by OJB's removal aware list.
176      */
177     private static void handleMembersUpdate(PeopleFlowBo peopleFlowBo, PeopleFlowDefinition peopleFlow) {
178 
179         Set<PeopleFlowMember> currentMembers = new HashSet<PeopleFlowMember>();
180 
181         if (null == peopleFlowBo.getMembers()) {
182             peopleFlowBo.setMembers(new ArrayList<PeopleFlowMemberBo>());
183         }
184         for (PeopleFlowMemberBo pplFlwMbr : peopleFlowBo.getMembers()) {
185             currentMembers.add(PeopleFlowMember.Builder.create(pplFlwMbr).build());
186         }
187 
188         if (!currentMembers.equals(new HashSet<PeopleFlowMember>(peopleFlow.getMembers()))) {
189             // this means that the membership has been updated, we need to rebuild it
190 //            peopleFlowBo.getMembers().clear();
191             ArrayList<PeopleFlowMemberBo> membersToAdd = new ArrayList<PeopleFlowMemberBo>();
192             for (PeopleFlowMember member : peopleFlow.getMembers()) {
193                 membersToAdd.add(PeopleFlowMemberBo.from(member, peopleFlowBo));
194             }
195             peopleFlowBo.setMembers(membersToAdd);
196         }
197     }
198 
199     public static PeopleFlowDefinition maintenanceCopy(PeopleFlowBo peopleFlowBo) {
200         if (null == peopleFlowBo) {
201             return null;
202         }
203         PeopleFlowDefinition.Builder builder = PeopleFlowDefinition.Builder.createMaintenanceCopy(peopleFlowBo);
204 
205         return builder.build();
206     }
207 
208     public static PeopleFlowDefinition to(PeopleFlowBo peopleFlowBo) {
209         if (null == peopleFlowBo) {
210             return null;
211         }
212         PeopleFlowDefinition.Builder builder = PeopleFlowDefinition.Builder.create(peopleFlowBo);
213 
214         return builder.build();
215     }
216 
217     /**
218      * Default constructor.
219      */
220     public PeopleFlowBo() { }
221 
222     public PeopleFlowBo(PeopleFlowDefinition pfDef) {
223         PeopleFlowBo newPFBo = new PeopleFlowBo();
224 
225         this.id = pfDef.getId();
226         this.active = pfDef.isActive();
227         this.name = pfDef.getName();
228         this.namespaceCode = pfDef.getNamespaceCode();
229         this.typeId = pfDef.getTypeId();
230         this.description = pfDef.getDescription();
231         this.versionNumber = pfDef.getVersionNumber();
232     }
233 
234     /**
235      * Invoked to rebuild the type attribute bos and attributes value map based on the type id
236      */
237     public void rebuildTypeAttributes() {
238         this.attributeBos = new ArrayList<PeopleFlowAttributeBo>();
239         this.attributeValues = new HashMap<String, String>();
240 
241         KewTypeDefinition typeDefinition = KewApiServiceLocator.getKewTypeRepositoryService().getTypeById(this.typeId);
242         if ((typeDefinition.getAttributes() != null) && !typeDefinition.getAttributes().isEmpty()) {
243             List<KewTypeAttribute> typeAttributes = new ArrayList<KewTypeAttribute>(typeDefinition.getAttributes());
244 
245             List<String> sortAttributes = new ArrayList<String>();
246             sortAttributes.add(KEWPropertyConstants.SEQUENCE_NUMBER);
247             Collections.sort(typeAttributes, new BeanPropertyComparator(sortAttributes));
248 
249             for (KewTypeAttribute typeAttribute: typeAttributes) {
250                 PeopleFlowAttributeBo attributeBo = PeopleFlowAttributeBo.from(typeAttribute.getAttributeDefinition(),
251                         null, this, null);
252                 this.attributeBos.add(attributeBo);
253                 this.attributeValues.put(typeAttribute.getAttributeDefinition().getName(), "");
254             }
255         }
256     }
257 
258     /**
259      * Updates the values in the attribute bos from the attribute values map.
260      */
261     public void updateAttributeBoValues() {
262         for (PeopleFlowAttributeBo attributeBo : this.attributeBos) {
263             if (this.attributeValues.containsKey(attributeBo.getAttributeDefinition().getName())) {
264                 String attributeValue = this.attributeValues.get(attributeBo.getAttributeDefinition().getName());
265                 attributeBo.setValue(attributeValue);
266             }
267         }
268     }
269 
270     /**
271      * Updates the values in the attribute values map from the attribute bos and updates the members.
272      */
273     @PostLoad
274     protected void postLoad() {
275         this.attributeValues = new HashMap<String, String>();
276         for (PeopleFlowAttributeBo attributeBo: attributeBos) {
277             this.attributeValues.put(attributeBo.getAttributeDefinition().getName(), attributeBo.getValue());
278         }
279         for (PeopleFlowMemberBo member: members) {
280             if (member.getMemberName() == null) {
281                 member.updateRelatedObject();
282             }
283             for (PeopleFlowDelegateBo delegate: member.getDelegates()) {
284                 if (delegate.getMemberName() == null) {
285                     delegate.updateRelatedObject();
286                 }
287             }
288         }
289     }
290 
291     public String getId() {
292         return id;
293     }
294 
295     public void setId(String id) {
296         this.id = id;
297     }
298 
299     public String getName() {
300         return name;
301     }
302 
303     public void setName(String name) {
304         this.name = name;
305     }
306 
307     public String getNamespaceCode() {
308         return namespaceCode;
309     }
310 
311     public void setNamespaceCode(String namespaceCode) {
312         this.namespaceCode = namespaceCode;
313     }
314 
315     public String getTypeId() {
316         return typeId;
317     }
318 
319     public void setTypeId(String typeId) {
320         this.typeId = typeId;
321     }
322 
323     public String getDescription() {
324         return description;
325     }
326 
327     public void setDescription(String description) {
328         this.description = description;
329     }
330 
331     public boolean isActive() {
332         return active;
333     }
334 
335     public void setActive(boolean active) {
336         this.active = active;
337     }
338 
339     public Long getVersionNumber() {
340         return versionNumber;
341     }
342 
343     public void setVersionNumber(Long versionNumber) {
344         this.versionNumber = versionNumber;
345     }
346 
347     public List<PeopleFlowAttributeBo> getAttributeBos() {
348         return this.attributeBos;
349     }
350 
351     @Override
352     public Map<String, String> getAttributes() {
353         Map<String, String> results = new HashMap<String, String>();
354 
355         if (null != this.attributeBos)
356             for (PeopleFlowAttributeBo attr: this.attributeBos) {
357                 results.put(attr.getAttributeDefinition().getName(), attr.getValue());
358             }
359 
360         return results;
361     }
362 
363     public void setAttributeBos(List<PeopleFlowAttributeBo> attributeBos) {
364         this.attributeBos = attributeBos;
365     }
366 
367     public List<PeopleFlowMemberBo> getMembers() {
368         return members;
369     }
370 
371     public void setMembers(List<PeopleFlowMemberBo> members) {
372         this.members = members;
373     }
374 
375     public Map<String, String> getAttributeValues() {
376         return attributeValues;
377     }
378 
379     public void setAttributeValues(Map<String, String> attributeValues) {
380         this.attributeValues = attributeValues;
381     }
382 
383     public KewTypeBo getTypeBo() {
384         return typeBo;
385     }
386 
387     public void setTypeBo(KewTypeBo typeBo) {
388         this.typeBo = typeBo;
389     }
390 }