View Javadoc

1   /**
2    * Copyright 2012 The Kuali Foundation
3    *
4    * Licensed under the the Educational Community License, Version 1.0
5    * (the "License"); you may not use this file except in compliance
6    * with the License.  You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl1.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.student.r2.common.helper;
17  
18  import java.util.ArrayList;
19  import java.util.LinkedHashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  /**
24   * 
25   * Helps perform merge operations that take place when an entity is loaded from
26   * the database and then updated from the dto content being provided through the
27   * service.
28   * 
29   * @author ocleirig
30   * 
31   * @param <E> The entity being merged into.
32   * @param <INFO> the dto providing the data to be merged.
33   */
34  public final class EntityMergeHelper<E, INFO> {
35  
36  	/**
37  	 * Options when merging the entity from the dto.
38  	 *
39  	 * @param <E>
40  	 * @param <INFO>
41  	 */
42  	public static interface EntityMergeOptions<E, INFO> {
43  		
44  		/**
45  		 * Extracts the id from the entity object.
46  		 * 
47  		 * @param entity the entity.
48  		 * @return the database id for the entity.
49  		 */
50  		public String getEntityId(E entity);
51  
52  		/**
53  		 * Extracts the id from the dto info object.
54  		 * 
55  		 * @param info the dto,
56  		 * @return the database id encoded in the dto.
57  		 */
58  		public String getInfoId(INFO info);
59  
60  		/**
61  		 * Merges the dto info into the entity.
62  		 * 
63  		 * @param entity the target entity.
64  		 * @param info the source dto
65  		 * @return the list of objects that should be detatched due to the merge.
66  		 */
67  		public List<Object> merge(E entity, INFO info);
68  
69  		/**
70  		 * Creates a new instance of the target entity.
71  		 * 
72  		 * @param info the source dto.
73  		 * @return the new entity.
74  		 */
75  		public E create(INFO info);
76  
77  	}
78  
79  	/**
80       * Options for merging a list of strings into a list of entities.
81  	 * 
82  	 * @param <E> The Entity object
83  	 */
84  	public static interface StringMergeOptions<E> {
85  
86  		/**
87  		 * Extract a comparison key from the entity that will match the value of the string list items (for comparison purposes)
88  		 * 
89  		 * @param entity
90  		 * @return the comnparison key.
91  		 */
92  		public String getKey(E entity);
93  
94  		/**
95  		 * Create a new entry based on the string value provided.
96  		 * 
97  		 * @param string
98  		 * @return the new entity.
99  		 */
100 		public E create(String value);
101 
102 	}
103 
104 	/**
105 	 * The results of a merge are the merged list and the list of orphaned data.
106 	 * 
107 	 * @param <E> the target entity.
108 	 */
109 	public static final class EntityMergeResult<E> {
110 
111 		private final List<E> mergedList;
112 
113 		private final List<Object> orphanList;
114 
115 		public EntityMergeResult(List<E> mergedList, List<Object> orphanList) {
116 			super();
117 			this.mergedList = mergedList;
118 			this.orphanList = orphanList;
119 		}
120 
121 		public List<E> getMergedList() {
122 			return mergedList;
123 		}
124 
125 		public List<Object> getOrphanList() {
126 			return orphanList;
127 		}
128 
129 	}
130 
131 	
132 	public EntityMergeHelper() {
133 	}
134 
135 	/**
136 	 * Merges a simple string list against a persisted list. We assume that if
137 	 * the string value does not exist then we can delete the item.
138 	 * 
139 	 * @param entityList the list of entities
140 	 * @param stringList the list of values
141 	 * @return the merge results.
142 	 */
143 	public EntityMergeResult<E> mergeStringList(List<E> entityList,
144 			List<String> stringList, StringMergeOptions<E> options) {
145 
146 		/*
147 		 * Steps:
148 		 * 1. create existing map
149 		 * 2. create new entries or remove existing from the map
150 		 * 3. return new entity list and orphan list.
151 		 */
152 		List<E> mergedList = new ArrayList<E>();
153 
154 		List<Object> orphanDataList = new ArrayList<Object>();
155 
156 		Map<String, E> existingEntityMap = new LinkedHashMap<String, E>();
157 
158 		if (entityList != null) {
159 			for (E e : entityList) {
160 
161 				String key = options.getKey(e);
162 
163 				existingEntityMap.put(key, e);
164 
165 			}
166 		}
167 		for (String string : stringList) {
168 
169 			E entity = existingEntityMap.get(string);
170 
171 			if (entity == null) {
172 				// create
173 
174 				entity = options.create(string);
175 
176 			} else {
177 				// remove from existing map
178 				existingEntityMap.remove(string);
179 			}
180 
181 			mergedList.add(entity);
182 
183 		}
184 
185 		orphanDataList.addAll(existingEntityMap.values());
186 
187 		return new EntityMergeResult<E>(mergedList, orphanDataList);
188 	}
189 
190 	/**
191 	 * Performs a Merge of the current entity list from the info list and
192 	 * options provided.
193 	 * 
194 	 * The results are the list of merged entities and a list of orphaned objects that can be deleted from the database.
195 	 * 
196 	 * @param entityList the target list of entities
197 	 * @param infoList the source list of dto info objects.
198 	 * @param options the logic for extracting the keys and creating/merging the entities.
199 	 * @return the results of the merge.
200 	 */
201 	public EntityMergeResult<E> merge(List<E> entityList, List<INFO> infoList,
202 			EntityMergeOptions<E, INFO> options) {
203 
204 		List<Object> orphanDataList = new ArrayList<Object>();
205 
206 		Map<String, E> existingEntityMap = new LinkedHashMap<String, E>();
207 
208 		if (entityList != null) {
209 			for (E e : entityList) {
210 
211 				String id = options.getEntityId(e);
212 
213 				existingEntityMap.put(id, e);
214 
215 			}
216 		}
217 
218 		List<E> mergedList = new ArrayList<E>();
219 
220 		for (INFO info : infoList) {
221 
222 			String id = options.getInfoId(info);
223 
224 			E entity = null;
225 			if (existingEntityMap.containsKey(id)) {
226 				// merge
227 				entity = existingEntityMap.remove(id);
228 
229 				orphanDataList.addAll(options.merge(entity, info));
230 
231 			} else {
232 				// new entry
233 
234 				entity = options.create(info);
235 			}
236 
237 			mergedList.add(entity);
238 		}
239 
240 		orphanDataList.addAll(existingEntityMap.values());
241 
242 		return new EntityMergeResult<E>(mergedList, orphanDataList);
243 
244 	}
245 
246 }