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}