1
2
3
4
5
6
7
8
9
10
11
12 package ca.spaz.cron.datasource;
13
14 import java.util.*;
15
16 import ca.spaz.cron.database.*;
17
18 /***
19 * A partial implementation of a mutable food source. Provides a certain
20 * degree of input validation and notification support.
21 *
22 * @author Chris Rose
23 */
24 public abstract class AbstractMutableFoodDatasource extends
25 AbstractFoodDataSource implements ILocalFoodDatasource {
26
27 private List listeners;
28
29 private boolean notify;
30
31 protected AbstractMutableFoodDatasource(String name) {
32 super(name);
33 }
34
35 protected final void setNotify(boolean notify) {
36 this.notify = notify;
37 }
38
39 protected final boolean getNotify() {
40 return notify;
41 }
42
43
44
45
46
47
48 public final Food addFood(Food food) {
49 notNull(food);
50 diffSource(food);
51
52 if (containsFood(food)) {
53 return findFoodBySourceUID(food.getSourceUID());
54 }
55 Food return_value = doAddFood(food);
56
57 return_value.setSourceUID(food.getSourceUID());
58 return return_value;
59 }
60
61 /***
62 * Find a local food by its sourceUID field value.
63 * @param sourceUID The key to search on
64 * @return The <code>Food</code> found, or <code>null</code> if no food matched.
65 */
66 protected abstract Food findFoodBySourceUID(String sourceUID);
67
68
69
70
71 public final Food createNewFood() {
72 Food return_value = doCreateNewFood();
73 return_value.setModified();
74 return return_value;
75 }
76
77 /***
78 * Create a new <code>Food</code> object associated with this datasource.
79 * @return a new <code>Food</code> instance.
80 */
81 protected abstract Food doCreateNewFood();
82
83
84
85
86
87
88 public final void addFoodDatasourceListener(IFoodDatasourceListener listener) {
89 getListeners().add(listener);
90 }
91
92
93
94
95 public final void addFoodGroup(FoodGroup foodGroup) {
96 doAddFoodGroup(foodGroup);
97 }
98
99 /***
100 * Add a new FoodGroup to the data source. The food group may already exist, in
101 * which case an implementing method should silently succeed.
102 *
103 * @param foodGroup the new food group to add to the data source.
104 */
105 protected abstract void doAddFoodGroup(FoodGroup foodGroup);
106
107
108
109
110
111
112
113 public final boolean addMeasure(Food food, Measure measure) {
114 notNull(food);
115 notNull(measure);
116 sameSource(food);
117 boolean succ = doAddMeasure(food, measure);
118 if (succ) {
119 food.setModified();
120 }
121 return succ;
122 }
123
124
125
126
127
128
129
130 public final boolean changeServingAmount(Serving serving) {
131 notNull(serving);
132 sameSource(serving.getFood());
133 return doChangeServingAmount(serving);
134 }
135
136
137
138
139
140
141 public final boolean saveFood(Food food) {
142 notNull(food);
143 sameSource(food);
144 return doSaveFood(food);
145 }
146
147
148
149
150
151
152
153 public final boolean changeMeasure(Food food, List measures) {
154 notNull(food);
155 notNull(measures);
156 sameSource(food);
157 boolean succ = doChangeMeasure(food, measures);
158 if (succ) {
159 food.setModified();
160 }
161 return succ;
162 }
163
164
165
166
167
168
169
170 public final Food addServing(Serving serving) {
171 notNull(serving);
172 if (containsFood(serving.getFood())) {
173 serving.setFood(findFoodBySourceUID(serving.getFood().getSourceUID()));
174 }
175 Food ret = doConsumeFood(serving);
176 serving.setFood(ret);
177 return ret;
178 }
179
180 /***
181 * Add a <code>Food</code> to this datasource.
182 *
183 * This method is an exception to the local datasource rule -- the <code>Food</code>
184 * provided to this method <em>must not</em> be attached to this datasource.
185 *
186 * @param food A <code>Food</code> object that comes from another datasource.
187 * @return a <code>Food</code> object whose information is the same as the parameter,
188 * but that exists only in this datasource, with all nutrient information still
189 * represtented. <code>null</code> will be returned if some error occurred.
190 */
191 protected abstract Food doAddFood(Food food);
192
193 /***
194 * Each food in the Datasource has available a list of <code>Measure</code>s that
195 * describe portions or servings. This method will add a valid <code>Measure</code>
196 * to a <code>Food</code> object stored in this datasource.
197 *
198 * @param food The <code>Food</code> object to which the new measure applies.
199 * @param measure The new <code>Measure</code>.
200 * @return <code>true</code> if successful, <code>false</code> if some error occurred.
201 */
202 protected abstract boolean doAddMeasure(Food food, Measure measure);
203
204 /***
205 * Changes the amount of a food consumed to the amount in the serving provided.
206 * This will alter the amount of the <code>Food</code> retrieved by calling
207 * <code>getFood()</code> on <code>newServing</code>. If the food is not already
208 * in the database as being consumed, it will be added. The food must already
209 * be in the user datasource, however.
210 *
211 * @param serving a <code>Serving</code> object whose <code>Food</code>'s serving
212 * quantity on the proper date will be altered.
213 * @return <code>true</code> if the serving was altered, <code>false</code> if some
214 * error occurred.
215 */
216 protected abstract boolean doChangeServingAmount(Serving serving);
217
218 /***
219 * Alter the information of some Food object in the backing representation of the
220 * datasource. This method has potential complications. If the
221 * <code>Food</code> implementation specific to the datasource has a concept of
222 * unique identifiers, a true alteration will occur. However, if the <code>Food</code>
223 * object does not have this feature, for example in a flat-file database or something
224 * like it, the implementor should check for name similarity, and if that does not
225 * match, simply add the new food to the database.
226 *
227 * @param food a <code>Food</code> object to be changed.
228 * @return <code>true</code> if successful, <code>false</code> if some error occurred.
229 */
230 protected abstract boolean doSaveFood(Food food);
231
232 /***
233 * @param food
234 * @param measures
235 * @return <code>true</code> if successful, <code>false</code> if some error occurred.
236 */
237 protected abstract boolean doChangeMeasure(Food food, List measures);
238
239 /***
240 * This method consumes the food in a serving. This an
241 * exception to the requirement that a <code>Food</code> object have
242 * this as its datasource, since in this case if the <code>Food</code>
243 * object has a different datasource, it will either A) be added to this
244 * one, and then consumed, or B) if it is already present in this datasource
245 * it will simply be added from this datasource.
246 *
247 * @param serving a <code>Serving</code> to add to the user's consumed list.
248 * @return A new <code>Food</code> object that represents the <code>Food</code>
249 * that is associated with this datasource. If the food was already associated
250 * with this datasource, the same object will be returned. <code>null</code>
251 * will be returned if some error occurred.
252 */
253 protected abstract Food doConsumeFood(Serving serving);
254
255 /***
256 * Retrieve the total number of times that this food has been consumed.
257 *
258 * @param food the <code>Food</code> to check.
259 * @return the number of times consumed, or <code>-1</code> on error.
260 */
261 protected abstract int doGetTimesConsumed(Food food);
262
263 /***
264 * Retrieve the number of times the specified <code>Food</code> was consumed
265 * between the provided dates.
266 *
267 * @param food the <code>Food</code> to check.
268 * @param startDate the starting date.
269 * @param endDate the ending date.
270 * @return the number of times consumed between the two dates, or <code>-1</code>
271 * on error.
272 */
273 protected abstract int doGetTimesConsumed(Food food, Date startDate,
274 Date endDate);
275
276 /***
277 * Remove a <code>Food</code> from the datasource.
278 *
279 * @param food the <code>Food</code> to remove.
280 * @return <code>true</code> if successful, <code>false</code> if some error occurred.
281 */
282 protected abstract boolean doRemoveFood(Food food);
283
284 /***
285 * Remove a particular form of <code>Measure</code> from the list of those available for
286 * a particular <code>Food</code>.
287 * @param food
288 * @param measure
289 * @return <code>true</code> if successful, <code>false</code> if some error occurred.
290 */
291 protected abstract boolean doRemoveMeasure(Food food, Measure measure);
292
293 /***
294 * Remove the <code>Food</code> in a particular serving from the user's consumed
295 * list. Operates regardless of quantity.
296 *
297 * @param serving the serving to un-consume.
298 * @return <code>true</code> if the action succeeded, <code>false</code> if there
299 * was some error.
300 */
301 protected abstract boolean doUnConsumeFood(Serving serving);
302
303 private List getListeners() {
304 if (null == listeners) {
305 listeners = new ArrayList();
306 }
307 return listeners;
308 }
309
310
311
312
313
314
315 public final int getTimesConsumed(Food food) {
316 notNull(food);
317 sameSource(food);
318 return doGetTimesConsumed(food);
319 }
320
321
322
323
324
325
326
327 public final int getTimesConsumed(Food food, Date startDate, Date endDate) {
328 sameSource(food);
329 return doGetTimesConsumed(food, startDate, endDate);
330 }
331
332 protected final void notifyObservers(FoodDataEvent type) {
333 notifyObservers(type, null);
334 }
335
336 protected final void notifyObservers(FoodDataEvent type, Object message) {
337 for (Iterator iter = getListeners().iterator(); iter.hasNext();) {
338 IFoodDatasourceListener list = (IFoodDatasourceListener) iter.next();
339 list.datasourceUpdated(this, type, message);
340 }
341 }
342
343
344
345
346
347
348 public final boolean removeFood(Food food) {
349 notNull(food);
350 sameSource(food);
351 return doRemoveFood(food);
352 }
353
354
355
356
357
358
359 public final void removeFoodDatasourceListener(
360 IFoodDatasourceListener listener) {
361 getListeners().remove(listener);
362 }
363
364
365
366
367
368
369
370 public final boolean removeMeasure(Food food, Measure measure) {
371 notNull(food);
372 notNull(measure);
373 sameSource(food);
374 boolean succ = doRemoveMeasure(food, measure);
375 if (succ) {
376 food.setModified();
377 }
378 return succ;
379 }
380
381
382
383
384
385
386 public final boolean removeServing(Serving serving) {
387 notNull(serving);
388 sameSource(serving.getFood());
389 return doUnConsumeFood(serving);
390 }
391 }