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