001/**
002 * Copyright 2005-2016 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.kew.impl.peopleflow;
017
018import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
019import org.kuali.rice.core.api.mo.common.active.MutableInactivatable;
020import org.kuali.rice.kew.api.KEWPropertyConstants;
021import org.kuali.rice.kew.api.KewApiServiceLocator;
022import org.kuali.rice.kew.api.peopleflow.PeopleFlowContract;
023import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
024import org.kuali.rice.kew.api.peopleflow.PeopleFlowMember;
025import org.kuali.rice.kew.api.repository.type.KewAttributeDefinition;
026import org.kuali.rice.kew.api.repository.type.KewTypeAttribute;
027import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
028import org.kuali.rice.kew.impl.type.KewTypeBo;
029import org.kuali.rice.krad.data.jpa.converters.BooleanYNConverter;
030import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
031import org.kuali.rice.krad.util.BeanPropertyComparator;
032
033import javax.persistence.CascadeType;
034import javax.persistence.Column;
035import javax.persistence.Convert;
036import javax.persistence.Entity;
037import javax.persistence.FetchType;
038import javax.persistence.GeneratedValue;
039import javax.persistence.Id;
040import javax.persistence.JoinColumn;
041import javax.persistence.ManyToOne;
042import javax.persistence.OneToMany;
043import javax.persistence.PostLoad;
044import javax.persistence.Table;
045import javax.persistence.Transient;
046import javax.persistence.Version;
047import java.io.Serializable;
048import java.util.ArrayList;
049import java.util.Collections;
050import java.util.HashMap;
051import java.util.HashSet;
052import java.util.List;
053import java.util.Map;
054import java.util.Set;
055
056/**
057 *  Mapped entity for PeopleFlows
058 *
059 *  @author Kuali Rice Team (rice.collab@kuali.org)
060 */
061@Entity
062@Table(name = "KREW_PPL_FLW_T")
063public class PeopleFlowBo implements Serializable, PeopleFlowContract, MutableInactivatable {
064
065    private static final long serialVersionUID = -4911187431645573793L;
066
067    @Id
068    @GeneratedValue(generator = "KREW_PPL_FLW_S")
069    @PortableSequenceGenerator(name = "KREW_PPL_FLW_S")
070    @Column(name = "PPL_FLW_ID", nullable = false)
071    private String id;
072
073    @Column(name = "NM", nullable = false)
074    private String name;
075
076    @Column(name = "NMSPC_CD", nullable = false)
077    private String namespaceCode;
078
079    @Column(name = "TYP_ID")
080    private String typeId;
081
082    @Column(name = "DESC_TXT")
083    private String description;
084
085    @Column(name = "ACTV", nullable = false)
086    @Convert(converter = BooleanYNConverter.class)
087    private boolean active = true;
088
089    @Version
090    @Column(name = "VER_NBR", nullable = false)
091    private Long versionNumber;
092
093    @ManyToOne
094    @JoinColumn(name = "TYP_ID", insertable = false, updatable = false)
095    private KewTypeBo typeBo;
096
097    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "peopleFlow", orphanRemoval = true)
098    private List<PeopleFlowAttributeBo> attributeBos = new ArrayList<PeopleFlowAttributeBo>();
099
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}