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.krad.util;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.List;
021
022import org.apache.log4j.Logger;
023import org.kuali.rice.krad.bo.PersistableBusinessObject;
024import org.springframework.orm.ObjectRetrievalFailureException;
025
026/**
027 * Helper object to deal with persisting collections.
028 */
029public 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}