EMMA Coverage Report (generated Fri Jan 29 20:09:05 CET 2016)
[all classes][kdk.android.simplydo]

COVERAGE SUMMARY FOR SOURCE FILE [CachingDataViewer.java]

nameclass, %method, %block, %line, %
CachingDataViewer.java100% (3/3)95%  (39/41)81%  (1214/1502)83%  (280,8/337)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class CachingDataViewer100% (1/1)94%  (29/31)80%  (1171/1459)83%  (276,8/333)
fetchItems (int): void 0%   (0/1)0%   (0/31)0%   (0/7)
fetchList (int): ListDesc 0%   (0/1)0%   (0/36)0%   (0/9)
itemIdBarrier (ItemDesc): void 100% (1/1)26%  (5/19)33%  (2/6)
flushTasksNoLock (): void 100% (1/1)59%  (10/17)57%  (3,4/6)
getItemData (): List 100% (1/1)67%  (10/15)67%  (2/3)
getListData (): List 100% (1/1)67%  (10/15)67%  (2/3)
flushTasks (): void 100% (1/1)69%  (11/16)92%  (3,7/4)
doTaskAndWait (CachingDataViewer$ViewerTask): void 100% (1/1)69%  (25/36)74%  (7,4/10)
dbUpdateLoop (): void 100% (1/1)75%  (284/381)79%  (59,1/75)
close (): void 100% (1/1)77%  (36/47)81%  (11,4/14)
createItem (String): void 100% (1/1)87%  (66/76)86%  (12,9/15)
deleteInactive (): void 100% (1/1)88%  (71/81)87%  (15,7/18)
moveItem (ItemDesc, int): void 100% (1/1)91%  (96/106)89%  (22,4/25)
deleteItem (ItemDesc): void 100% (1/1)91%  (49/54)99%  (11,9/12)
updateItemLabel (ItemDesc, String): void 100% (1/1)91%  (49/54)99%  (10,9/11)
updateListLabel (int, String): void 100% (1/1)91%  (59/65)87%  (11,4/13)
updateItemStarness (ItemDesc, boolean): void 100% (1/1)91%  (50/55)99%  (10,9/11)
createList (String): void 100% (1/1)91%  (51/56)99%  (11,9/12)
deleteList (int): void 100% (1/1)93%  (62/67)97%  (14,6/15)
updateItemActiveness (ItemDesc, boolean): void 100% (1/1)93%  (69/74)96%  (14,4/15)
CachingDataViewer (DataManager): void 100% (1/1)100% (42/42)100% (10/10)
access$000 (CachingDataViewer): void 100% (1/1)100% (3/3)100% (1/1)
fetchLists (): void 100% (1/1)100% (21/21)100% (6/6)
findList (int): ListDesc 100% (1/1)100% (23/23)100% (7/7)
flush (): void 100% (1/1)100% (3/3)100% (2/2)
getSelectedList (): ListDesc 100% (1/1)100% (3/3)100% (1/1)
invalidateCache (): void 100% (1/1)100% (6/6)100% (3/3)
setSelectedList (ListDesc): void 100% (1/1)100% (18/18)100% (6/6)
start (): void 100% (1/1)100% (7/7)100% (3/3)
updateListStats (): void 100% (1/1)100% (5/5)100% (2/2)
updateListStats (ListDesc): void 100% (1/1)100% (27/27)100% (8/8)
     
class CachingDataViewer$1100% (1/1)100% (2/2)100% (10/10)100% (3/3)
CachingDataViewer$1 (CachingDataViewer): void 100% (1/1)100% (6/6)100% (1/1)
run (): void 100% (1/1)100% (4/4)100% (2/2)
     
class CachingDataViewer$ViewerTask100% (1/1)100% (8/8)100% (33/33)100% (2/2)
CachingDataViewer$ViewerTask (): void 100% (1/1)100% (6/6)100% (2/2)
CachingDataViewer$ViewerTask (CachingDataViewer$1): void 100% (1/1)100% (3/3)100% (1/1)
access$200 (CachingDataViewer$ViewerTask): int 100% (1/1)100% (3/3)100% (1/1)
access$202 (CachingDataViewer$ViewerTask, int): int 100% (1/1)100% (5/5)100% (1/1)
access$300 (CachingDataViewer$ViewerTask): Object [] 100% (1/1)100% (3/3)100% (1/1)
access$302 (CachingDataViewer$ViewerTask, Object []): Object [] 100% (1/1)100% (5/5)100% (1/1)
access$400 (CachingDataViewer$ViewerTask): boolean 100% (1/1)100% (3/3)100% (1/1)
access$402 (CachingDataViewer$ViewerTask, boolean): boolean 100% (1/1)100% (5/5)100% (1/1)

1/*
2 * Copyright (C) 2010, 2011 Keith Kildare
3 * 
4 * This file is part of SimplyDo.
5 * 
6 * SimplyDo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * 
11 * SimplyDo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 * 
16 * You should have received a copy of the GNU General Public License
17 * along with SimplyDo.  If not, see <http://www.gnu.org/licenses/>.
18 * 
19 */
20package kdk.android.simplydo;
21 
22import java.util.ArrayList;
23import java.util.LinkedList;
24import java.util.List;
25 
26import android.util.Log;
27 
28/**
29 * A cache of the database data which does the actual database calls lazily on
30 * a separate thread. On my device a database write take 300ms-450ms which is
31 * too log to hang onto the UI thread for.
32 */
33public class CachingDataViewer implements DataViewer
34{
35    private DataManager dataManager;
36    
37    private List<ItemDesc> itemData = new LinkedList<ItemDesc>();
38    private List<ListDesc> listData = new ArrayList<ListDesc>();
39    
40    private Thread dbUpdateThread;
41    private Object viewerLock = new Object();
42    private LinkedList<ViewerTask> taskQueue = new LinkedList<ViewerTask>();
43 
44    // declared volatile since it is accessed in the db thread
45    // without holding the viewerLock
46    private volatile boolean running = false;
47    
48    private boolean interruptRequire = false;
49    private ListDesc selectedList;
50 
51    
52    public CachingDataViewer(DataManager dataManager)
53    {
54        this.dataManager = dataManager;
55        
56        dbUpdateThread = new Thread(new Runnable() {
57            @Override
58            public void run()
59            {
60                dbUpdateLoop();
61            }
62        }, "DB Update");
63    }
64    
65    
66    public void start()
67    {
68        running = true;
69        // start task queue
70        
71        dbUpdateThread.start();
72    }
73    
74    @Override
75    public void invalidateCache()
76    {
77        setSelectedList(null);
78        fetchLists();
79    }
80    
81    @Override
82    public void flush()
83    {
84        flushTasks();
85    }
86    
87    @Override
88    public void close()
89    {
90        Log.v(L.TAG, "CachingDataView.close(): Entered");
91        synchronized (viewerLock)
92        {
93            flushTasksNoLock();
94            
95            running = false;
96            
97            if(interruptRequire)
98            {
99                Log.d(L.TAG, "CachingDataView.close(): Close interrupt required");
100                dbUpdateThread.interrupt();
101            }
102        }        
103        
104        try
105        {
106            dbUpdateThread.join();
107        }
108        catch (InterruptedException e)
109        {
110            Log.d(L.TAG, "CachingDataView.close(): shutdown join interrupted", e);
111        }
112        
113        Log.v(L.TAG, "CachingDataView.close(): Exit");
114    }
115 
116 
117    @Override
118    public List<ItemDesc> getItemData()
119    {
120        synchronized (viewerLock)
121        {
122            return itemData;
123        }
124    }
125 
126    
127    @Override
128    public List<ListDesc> getListData()
129    {
130        synchronized (viewerLock)
131        {
132            return listData;
133        }
134    }
135    
136    
137    @Override
138    public ListDesc getSelectedList()
139    {
140        return selectedList;
141    }
142 
143 
144    @Override
145    public void setSelectedList(ListDesc selectedList)
146    {
147        flushTasks();
148        
149        // this is ok since this thread is the only source
150        // of task and we've just flushed the task queue
151        itemData.clear();
152        if(selectedList != null)
153        {
154            dataManager.fetchItems(selectedList.getId(), itemData);
155        }
156        
157        this.selectedList = selectedList;
158    }
159 
160 
161    @Override
162    public void fetchLists()
163    {
164        Log.v(L.TAG, "CachingDataView.fetchLists(): Entered");
165        ViewerTask task = new ViewerTask();
166        task.taskId = ViewerTask.FETCH_LISTS;
167        doTaskAndWait(task);
168        Log.v(L.TAG, "CachingDataView.fetchLists(): Exited");
169    }
170    
171    @Override
172        public ListDesc fetchList(int listId)
173    {
174        ListDesc rv = null;
175        
176        synchronized (viewerLock)
177        {
178            for(ListDesc list : listData)
179            {
180                if(listId == list.getId())
181                {
182                    rv = list;
183                    break;
184                }
185            }
186        }
187 
188        return rv;
189    }
190 
191    
192    @Override
193    public void fetchItems(int listId)
194    {
195        Log.v(L.TAG, "CachingDataViewer.fetchItems(): Entered");
196        ViewerTask task = new ViewerTask();
197        task.taskId = ViewerTask.FETCH_ITEMS;
198        task.args = new Object[]{listId};
199        doTaskAndWait(task);
200        Log.v(L.TAG, "CachingDataViewer.fetchItems(): Exited");
201    }
202    
203 
204    @Override
205    public void createItem(String label)
206    {
207        if(selectedList == null)
208        {
209            Log.e(L.TAG, "CachingDataViewer.createItem(): called but no list is selected");
210            return;
211        }
212        
213        int listId = selectedList.getId();
214        
215        ItemDesc newItem = new ItemDesc(-1, label, true, false);
216        
217        ViewerTask createTask = new ViewerTask();
218        createTask.taskId = ViewerTask.CREATE_ITEM;
219        createTask.args = new Object[]{listId, label, newItem};
220        
221        synchronized (viewerLock)
222        {
223            // queue fetch lists
224            taskQueue.add(createTask);
225            viewerLock.notifyAll();
226            
227            itemData.add(0, newItem);
228            
229            updateListStats();
230        }
231    }
232 
233 
234    @Override
235    public void createList(String label)
236    {
237        ViewerTask createTask = new ViewerTask();
238        createTask.taskId = ViewerTask.CREATE_LIST;
239        createTask.args = new Object[]{label};
240 
241        ViewerTask fetchTask = new ViewerTask();
242        fetchTask.taskId = ViewerTask.FETCH_LISTS;
243        
244        synchronized (viewerLock)
245        {
246            // queue fetch lists
247            taskQueue.add(createTask);
248            taskQueue.add(fetchTask);
249            viewerLock.notifyAll();
250            
251            flushTasksNoLock();
252        }
253    }
254 
255 
256    @Override
257    public void deleteInactive()
258    {
259        if(selectedList == null)
260        {
261            Log.e(L.TAG, "CachingDataViewer.deleteInactive() called but no list is selected");
262            return;
263        }
264        
265        ViewerTask task = new ViewerTask();
266        task.taskId = ViewerTask.DELETE_INACTIVE;
267        task.args = new Object[]{selectedList.getId()};
268 
269        synchronized (viewerLock)
270        {
271            // queue fetch lists
272            taskQueue.add(task);
273            viewerLock.notifyAll();
274            
275            // update items data
276            List<ItemDesc> toDelete = new ArrayList<ItemDesc>();
277            for(ItemDesc i : itemData)
278            {
279                if(!i.isActive())
280                {
281                    toDelete.add(i);
282                }
283            }
284            itemData.removeAll(toDelete);
285            
286            updateListStats();
287        }
288    }
289 
290 
291    @Override
292    public void deleteItem(ItemDesc item)
293    {
294        itemIdBarrier(item);
295        
296        int itemId = item.getId();        
297        
298        ViewerTask task = new ViewerTask();
299        task.taskId = ViewerTask.DELETE_ITEM;
300        task.args = new Object[]{itemId};
301        
302        synchronized (viewerLock)
303        {
304            // queue fetch lists
305            taskQueue.add(task);
306            viewerLock.notifyAll();
307            
308            // update items data
309            itemData.remove(item);
310            updateListStats();
311        }
312    }
313 
314 
315    @Override
316    public void deleteList(int listId)
317    {
318        ViewerTask task = new ViewerTask();
319        task.taskId = ViewerTask.DELETE_LIST;
320        task.args = new Object[]{listId};
321        
322        synchronized (viewerLock)
323        {
324            // queue fetch lists
325            taskQueue.add(task);
326            viewerLock.notifyAll();
327            
328            // update lists data
329            ListDesc delete = null;
330            for(ListDesc list : listData)
331            {
332                if(listId == list.getId())
333                {
334                    delete = list;
335                    break;
336                }
337            }
338            listData.remove(delete);
339        }
340        
341    }
342 
343 
344    @Override
345    public void updateItemLabel(ItemDesc item, String newLabel)
346    {
347        itemIdBarrier(item);
348        int itemId = item.getId();
349        
350        ViewerTask task = new ViewerTask();
351        task.taskId = ViewerTask.UPDATE_ITEM_LABEL;
352        task.args = new Object[]{itemId, newLabel};
353        
354        synchronized (viewerLock)
355        {
356            // queue fetch lists
357            taskQueue.add(task);
358            viewerLock.notifyAll();
359            
360            // update items data
361            item.setLabel(newLabel);
362        }        
363    }
364 
365 
366    @Override
367    public void moveItem(ItemDesc item, int toListId)
368    {
369        itemIdBarrier(item);
370        int itemId = item.getId();
371 
372        ViewerTask task = new ViewerTask();
373        task.taskId = ViewerTask.MOVE_ITEM;
374        task.args = new Object[]{itemId, toListId};
375        
376        synchronized (viewerLock)
377        {
378            // queue fetch lists
379            taskQueue.add(task);
380            viewerLock.notifyAll();
381            
382            // remove from items data
383            ItemDesc itemInList = null;
384            for(ItemDesc i : itemData)
385            {
386                if(itemId == i.getId())
387                {
388                    itemInList = i;
389                    break;
390                }
391            }
392            if(itemInList != null)
393            {
394                itemData.remove(itemInList);
395                updateListStats();
396                ListDesc toList = findList(toListId);
397                toList.setTotalItems(toList.getTotalItems() + 1);
398                if(itemInList.isActive())
399                {
400                    toList.setActiveItems(toList.getActiveItems() + 1);
401                }
402            }
403            else
404            {
405                Log.w(L.TAG, "CachingDataViewer.moveItem(): Didn't find item in current item data");
406            }
407        }        
408    }
409 
410 
411    @Override
412    public void updateListLabel(int listId, String newLabel)
413    {
414        ViewerTask task = new ViewerTask();
415        task.taskId = ViewerTask.UPDATE_LIST_LABEL;
416        task.args = new Object[]{listId, newLabel};
417        
418        synchronized (viewerLock)
419        {
420            // queue fetch lists
421            taskQueue.add(task);
422            viewerLock.notifyAll();
423            
424            // update items data
425            for(ListDesc i : listData)
426            {
427                if(listId == i.getId())
428                {
429                    i.setLabel(newLabel);
430                    break;
431                }
432            }
433        }        
434    }
435    
436 
437    @Override
438    public void updateItemActiveness(ItemDesc item, boolean active)
439    {
440        itemIdBarrier(item);
441        int itemId = item.getId();
442        
443        ViewerTask task = new ViewerTask();
444        task.taskId = ViewerTask.UPDATE_ACTIVENESS;
445        task.args = new Object[]{itemId, active};
446        
447        synchronized (viewerLock)
448        {
449            // queue fetch lists
450            taskQueue.add(task);
451            viewerLock.notifyAll();
452            
453            // update items data
454            item.setActive(active);
455            
456            // update lists data
457            if(selectedList != null)
458            {
459                int activeItems = selectedList.getActiveItems();
460                activeItems += active?1:-1;
461                selectedList.setActiveItems(activeItems);
462            }
463        }
464    }
465    
466 
467    @Override
468    public void updateItemStarness(ItemDesc item, boolean star)
469    {
470        itemIdBarrier(item);
471        int itemId = item.getId();
472        
473        ViewerTask task = new ViewerTask();
474        task.taskId = ViewerTask.UPDATE_STARNESS;
475        task.args = new Object[]{itemId, star};
476        
477        synchronized (viewerLock)
478        {
479            // queue fetch lists
480            taskQueue.add(task);
481            viewerLock.notifyAll();
482            
483            // update items data
484            item.setStar(star);
485        }
486    }
487    
488    
489    private void itemIdBarrier(ItemDesc item)
490    {
491        if(item.getId() == -1)
492        {
493            Log.v(L.TAG, "CachingDataViewer.itemIdBarrier(): Used");
494            flushTasks();
495            
496            if(item.getId() == -1)
497            {
498                Log.e(L.TAG, "CachingDataViewer.itemIdBarrier(): failed!");
499            }
500        }
501    }
502    
503    
504    private void flushTasksNoLock()
505    {
506        while(taskQueue.size() != 0)
507        {
508            try
509            {
510                viewerLock.wait(200);
511            }
512            catch (InterruptedException e)
513            {
514                Log.e(L.TAG, "CachingDataViewer.flushTasksNoLock(): Exception waiting for flushTasksNoLock()", e);
515            }
516        }
517    }
518    
519    private void flushTasks()
520    {
521        synchronized (viewerLock)
522        {
523            flushTasksNoLock();
524        }
525    }
526    
527    private void updateListStats()
528    {
529        updateListStats(selectedList);
530    }
531    
532    private ListDesc findList(int listId)
533    {
534        ListDesc rvList = null;
535        for(ListDesc list : listData)
536        {
537            if(list.getId() == listId)
538            {
539                rvList = list;
540                break;
541            }
542        }
543 
544        return rvList;
545    }
546    
547    
548    private void updateListStats(ListDesc listDesc)
549    {
550        listDesc.setTotalItems(itemData.size());
551        int active = 0;
552        for(ItemDesc item : itemData)
553        {
554            if(item.isActive())
555            {
556                active++;
557            }
558        }
559        listDesc.setActiveItems(active);
560    }
561    
562    
563    private void doTaskAndWait(ViewerTask task)
564    {
565        synchronized (viewerLock)
566        {
567            taskQueue.add(task);
568            viewerLock.notifyAll();
569            
570            try
571            {
572                while(!task.done)
573                {
574                    viewerLock.wait();
575                }
576            }
577            catch(InterruptedException e)
578            {
579                Log.e(L.TAG, "CachingDataViewer.doTaskAndWait(): Error waiting for task", e);
580            }
581        }
582    }
583    
584    
585    private void dbUpdateLoop()
586    {
587        Log.v(L.TAG, "CachingDataViewer.dbUpdateLoop(): Entered");
588        while(running)
589        {
590            try
591            {
592                // get task
593                ViewerTask task;
594                synchronized (viewerLock)
595                {
596                    while(taskQueue.size() == 0)
597                    {
598                        interruptRequire = true;
599                        viewerLock.wait();
600                        interruptRequire = false;
601                    }
602                    task = taskQueue.peek();
603                }
604                
605                boolean doNotify = true;
606                try
607                {
608                    // do it
609                    switch(task.taskId)
610                    {
611                    case ViewerTask.FETCH_LISTS:
612                    {
613                        List<ListDesc> lists = dataManager.fetchLists();
614                        synchronized (viewerLock)
615                        {
616                            listData = lists;
617                            task.done = true;
618                            viewerLock.notifyAll();
619                        }
620                        doNotify = false;
621                        break;
622                    }
623                    case ViewerTask.FETCH_ITEMS:
624                    {
625                        synchronized (viewerLock)
626                        {
627                            itemData.clear();
628                            dataManager.fetchItems((Integer)task.args[0], itemData);
629                            task.done = true;
630                            viewerLock.notifyAll();
631                        }
632                        doNotify = false;
633                        break;
634                    }
635                    case ViewerTask.UPDATE_ACTIVENESS:
636                    {
637                        dataManager.updateItemActiveness((Integer)task.args[0], (Boolean)task.args[1]);
638                        task.done = true;
639                        break;
640                    }
641                    case ViewerTask.UPDATE_STARNESS:
642                    {
643                        dataManager.updateItemStarness((Integer)task.args[0], (Boolean)task.args[1]);
644                        task.done = true;
645                        break;
646                    }
647                    case ViewerTask.UPDATE_ITEM_LABEL:
648                    {
649                        dataManager.updateItemLabel((Integer)task.args[0], (String)task.args[1]);
650                        task.done = true;
651                        break;
652                    }
653                    case ViewerTask.UPDATE_LIST_LABEL:
654                    {
655                        dataManager.updateListLabel((Integer)task.args[0], (String)task.args[1]);
656                        task.done = true;
657                        break;
658                    }
659                    case ViewerTask.MOVE_ITEM:
660                    {
661                        dataManager.moveItem((Integer)task.args[0], (Integer)task.args[1]);
662                        task.done = true;
663                        break;
664                    }
665                    case ViewerTask.DELETE_LIST:
666                    {
667                        dataManager.deleteList((Integer)task.args[0]);
668                        task.done = true;
669                        break;
670                    }
671                    case ViewerTask.DELETE_ITEM:
672                    {
673                        dataManager.deleteItem((Integer)task.args[0]);
674                        task.done = true;
675                        break;
676                    }
677                    case ViewerTask.DELETE_INACTIVE:
678                    {
679                        dataManager.deleteInactive((Integer)task.args[0]);
680                        task.done = true;
681                        break;
682                    }
683                    case ViewerTask.CREATE_LIST:
684                    {
685                        dataManager.createList((String)task.args[0]);
686                        task.done = true;
687                        break;
688                    }
689                    case ViewerTask.CREATE_ITEM:
690                    {
691                        ItemDesc item = (ItemDesc)task.args[2];
692                        int id = dataManager.createItem((Integer)task.args[0], (String)task.args[1]);
693                        item.setId(id);
694                        task.done = true;
695                        break;
696                    }
697                    default:
698                        Log.w(L.TAG, "CachingDataViewer.dbUpdateLoop(): Unknown task enumeration " + task.taskId);
699                    }
700                }
701                finally
702                {
703                    synchronized (viewerLock)
704                    {
705                        taskQueue.remove();                
706                        if(doNotify)
707                        {
708                            viewerLock.notifyAll();
709                        }
710                    }
711                }                
712            }
713            catch(InterruptedException e)
714            {
715                if(running)
716                {
717                    Log.e(L.TAG, "CachingDataViewer.dbUpdateLoop(): Interrupted in DB update loop", e);
718                }
719                else
720                {
721                    Log.d(L.TAG, "CachingDataViewer.dbUpdateLoop(): interrupt exit");
722                }
723            }
724            catch(Exception e)
725            {
726                Log.e(L.TAG, "CachingDataViewer.dbUpdateLoop(): Exception in DB update loop", e);
727            }
728        }
729        Log.v(L.TAG, "CachingDataViewer.dbUpdateLoop(): Exit");
730    }
731    
732    
733    private static class ViewerTask
734    {
735        private static final int FETCH_LISTS = 0;
736        private static final int FETCH_ITEMS = 1;
737        private static final int UPDATE_ACTIVENESS = 2;
738        private static final int UPDATE_ITEM_LABEL = 3;
739        private static final int UPDATE_LIST_LABEL = 4;
740        private static final int DELETE_LIST = 5;
741        private static final int DELETE_ITEM = 6;
742        private static final int DELETE_INACTIVE = 7;
743        private static final int CREATE_LIST = 8;
744        private static final int CREATE_ITEM = 9;
745        private static final int UPDATE_STARNESS = 10;
746        private static final int MOVE_ITEM = 11;
747        
748        private int taskId;
749        private Object[] args;
750        private boolean done = false;
751    }
752 
753}

[all classes][kdk.android.simplydo]
EMMA 2.0.5312 (C) Vladimir Roubtsov