View Javadoc

1   /*
2    *******************************************************************************
3    * Copyright (c) 2005 Chris Rose and AIMedia
4    * All rights reserved. AbstractMutableFoodDatasource and the accompanying materials
5    * are made available under the terms of the Common Public License v1.0
6    * which accompanies this distribution, and is available at
7    * http://www.eclipse.org/legal/cpl-v10.html
8    * 
9    * Contributors:
10   *     Chris Rose
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      * (non-Javadoc)
45      * 
46      * @see ca.spaz.cron.datasource.ILocalFoodDatasource#addFood(ca.spaz.cron.database.Food)
47      */
48     public final Food addFood(Food food) {
49        notNull(food);
50        diffSource(food);
51        /* If we already have this food in the data source, return the local version of it. */
52        if (containsFood(food)) {
53           return findFoodBySourceUID(food.getSourceUID());
54        }
55        Food return_value = doAddFood(food);
56        /* Copy the sourceUID from the added Food to this new food */
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     /* (non-Javadoc)
69      * @see ca.spaz.cron.datasource.ILocalFoodDatasource#createNewFood()
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      * (non-Javadoc)
85      * 
86      * @see ca.spaz.cron.datasource.ILocalFoodDatasource#addFoodDatasourceListener(ca.spaz.cron.datasource.IFoodDatasourceListener)
87      */
88     public final void addFoodDatasourceListener(IFoodDatasourceListener listener) {
89        getListeners().add(listener);
90     }
91     
92     /* (non-Javadoc)
93      * @see ca.spaz.cron.datasource.ILocalFoodDatasource#addFoodGroup(ca.spaz.cron.database.FoodGroup)
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     * (non-Javadoc)
109     * 
110     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#addMeasure(ca.spaz.cron.database.Food,
111     *      ca.spaz.cron.database.Measure)
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     * (non-Javadoc)
126     * 
127     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#changeConsumedAmount(ca.spaz.cron.database.Food,
128     *      ca.spaz.cron.database.Serving)
129     */
130    public final boolean changeServingAmount(Serving serving) {
131       notNull(serving);
132       sameSource(serving.getFood());
133       return doChangeServingAmount(serving);
134    }
135 
136    /*
137     * (non-Javadoc)
138     * 
139     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#changeFood(ca.spaz.cron.database.Food)
140     */
141    public final boolean saveFood(Food food) {
142       notNull(food);
143       sameSource(food);
144       return doSaveFood(food);
145    }
146 
147    /*
148     * (non-Javadoc)
149     * 
150     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#changeMeasure(ca.spaz.cron.database.Food,
151     *      java.util.List)
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     * (non-Javadoc)
166     * 
167     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#consumeFood(ca.spaz.cron.database.Food,
168     *      ca.spaz.cron.database.Serving)
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     * (non-Javadoc)
312     * 
313     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#getTimesConsumed(ca.spaz.cron.database.Food)
314     */
315    public final int getTimesConsumed(Food food) {
316       notNull(food);
317       sameSource(food);
318       return doGetTimesConsumed(food);
319    }
320 
321    /*
322     * (non-Javadoc)
323     * 
324     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#getTimesConsumed(ca.spaz.cron.database.Food,
325     *      java.util.Date, java.util.Date)
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     * (non-Javadoc)
345     * 
346     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#removeFood(ca.spaz.cron.database.Food)
347     */
348    public final boolean removeFood(Food food) {
349       notNull(food);
350       sameSource(food);
351       return doRemoveFood(food);
352    }
353 
354    /*
355     * (non-Javadoc)
356     * 
357     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#removeFoodDatasourceListener(ca.spaz.cron.datasource.IFoodDatasourceListener)
358     */
359    public final void removeFoodDatasourceListener(
360          IFoodDatasourceListener listener) {
361       getListeners().remove(listener);
362    }
363 
364    /*
365     * (non-Javadoc)
366     * 
367     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#removeMeasure(ca.spaz.cron.database.Food,
368     *      ca.spaz.cron.database.Measure)
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     * (non-Javadoc)
383     * 
384     * @see ca.spaz.cron.datasource.ILocalFoodDatasource#unConsumeFood(ca.spaz.cron.database.Food)
385     */
386    public final boolean removeServing(Serving serving) {
387       notNull(serving);
388       sameSource(serving.getFood());
389       return doUnConsumeFood(serving);
390    }
391 }