001 /* 002 * Copyright 2011 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 */ 016 package org.kuali.kfs.module.purap.util; 017 018 import java.util.ArrayList; 019 import java.util.Collection; 020 import java.util.Iterator; 021 import java.util.List; 022 023 import org.kuali.rice.kns.bo.PersistableBusinessObject; 024 import org.kuali.rice.kns.util.ObjectUtils; 025 import org.kuali.rice.kns.util.OjbCollectionAware; 026 import org.springframework.orm.ObjectRetrievalFailureException; 027 028 /** 029 * Helper object to deal with persisting collections. 030 */ 031 public class PurApOjbCollectionHelper { 032 public final static int MAX_DEPTH = 2; 033 034 /** 035 * OJB RemovalAwareLists do not survive through the response/request lifecycle. This method is a work-around to forcibly remove 036 * business objects that are found in Collections stored in the database but not in memory. 037 * 038 * @param orig 039 * @param id 040 * @param template 041 */ 042 public void processCollections(OjbCollectionAware template, PersistableBusinessObject orig, PersistableBusinessObject copy) { 043 processCollectionsRecurse(template, orig, copy, MAX_DEPTH); 044 } 045 046 /** 047 * This method processes collections recursively up to the depth level specified 048 * 049 * @param template 050 * @param orig 051 * @param copy 052 */ 053 private void processCollectionsRecurse(OjbCollectionAware template, PersistableBusinessObject orig, PersistableBusinessObject copy, int depth) { 054 if (copy == null || depth < 1) { 055 return; 056 } 057 058 List originalCollections = orig.buildListOfDeletionAwareLists(); 059 060 if (originalCollections != null && !originalCollections.isEmpty()) { 061 /* 062 * Prior to being saved, the version in the database will not yet reflect any deleted collections. So, a freshly 063 * retrieved version will contain objects that need to be removed: 064 */ 065 try { 066 List copyCollections = copy.buildListOfDeletionAwareLists(); 067 int size = originalCollections.size(); 068 069 if (copyCollections.size() != size) { 070 throw new RuntimeException("size mismatch while attempting to process list of Collections to manage"); 071 } 072 073 for (int i = 0; i < size; i++) { 074 Collection<PersistableBusinessObject> origSource = (Collection<PersistableBusinessObject>) originalCollections.get(i); 075 Collection<PersistableBusinessObject> copySource = (Collection<PersistableBusinessObject>) copyCollections.get(i); 076 List list = findUnwantedElements(copySource, origSource, template, depth - 1); 077 cleanse(template, origSource, list); 078 079 } 080 } 081 catch (ObjectRetrievalFailureException orfe) { 082 // object wasn't found, must be pre-save 083 } 084 } 085 } 086 087 /** 088 * OJB RemovalAwareLists do not survive through the response/request lifecycle. This method is a work-around to forcibly remove 089 * business objects that are found in Collections stored in the database but not in memory. 090 * 091 * @param orig 092 * @param id 093 * @param template 094 */ 095 public void processCollections2(OjbCollectionAware template, PersistableBusinessObject orig, PersistableBusinessObject copy) { 096 // if copy is null this is the first time we are saving the object, don't have to worry about updating collections 097 if (copy == null) { 098 return; 099 } 100 101 List originalCollections = orig.buildListOfDeletionAwareLists(); 102 103 if (originalCollections != null && !originalCollections.isEmpty()) { 104 /* 105 * Prior to being saved, the version in the database will not yet reflect any deleted collections. So, a freshly 106 * retrieved version will contain objects that need to be removed: 107 */ 108 try { 109 List copyCollections = copy.buildListOfDeletionAwareLists(); 110 int size = originalCollections.size(); 111 112 if (copyCollections.size() != size) { 113 throw new RuntimeException("size mismatch while attempting to process list of Collections to manage"); 114 } 115 116 for (int i = 0; i < size; i++) { 117 Collection origSource = (Collection) originalCollections.get(i); 118 Collection copySource = (Collection) copyCollections.get(i); 119 List list = findUnwantedElements(copySource, origSource, null, 0); 120 cleanse(template, origSource, list); 121 122 } 123 } 124 catch (ObjectRetrievalFailureException orfe) { 125 // object wasn't found, must be pre-save 126 } 127 } 128 } 129 130 /** 131 * This method deletes unwanted objects from the database as well as from the given input List 132 * 133 * @param origSource - list containing unwanted business objects 134 * @param unwantedItems - business objects to be permanently removed 135 * @param template 136 */ 137 private void cleanse(OjbCollectionAware template, Collection origSource, List unwantedItems) { 138 if (unwantedItems.size() > 0) { 139 Iterator iter = unwantedItems.iterator(); 140 while (iter.hasNext()) { 141 template.getPersistenceBrokerTemplate().delete(iter.next()); 142 } 143 } 144 145 } 146 147 /** 148 * This method identifies items in the first List that are not contained in the second List. It is similar to the (optional) 149 * java.util.List retainAll method. 150 * 151 * @param fromList list from the database 152 * @param controlList list from the object 153 * @return true iff one or more items were removed 154 */ 155 private List findUnwantedElements(Collection fromList, Collection controlList, OjbCollectionAware template, int depth) { 156 List toRemove = new ArrayList(); 157 158 Iterator iter = fromList.iterator(); 159 while (iter.hasNext()) { 160 PersistableBusinessObject copyLine = (PersistableBusinessObject) iter.next(); 161 162 PersistableBusinessObject line = (PersistableBusinessObject) PurApObjectUtils.retrieveObjectWithIdentitcalKey(controlList, copyLine); 163 if (ObjectUtils.isNull(line)) { 164 toRemove.add(copyLine); 165 } 166 else { // since we're not deleting try to recurse on this element 167 processCollectionsRecurse(template, line, copyLine, depth); 168 } 169 } 170 return toRemove; 171 } 172 }