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 }