EMMA Coverage Report (generated Mon Mar 20 16:21:22 CET 2017)
[all classes][kdk.android.simplydo]

COVERAGE SUMMARY FOR SOURCE FILE [CachingDataViewer.java]

nameclass, %method, %block, %line, %
CachingDataViewer.java100% (3/3)95%  (39/41)80%  (1206/1502)83%  (280.8/337)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class CachingDataViewer100% (1/1)94%  (29/31)80%  (1163/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)
deleteInactive (): void 100% (1/1)83%  (67/81)81%  (14.7/18)
moveItem (ItemDesc, int): void 100% (1/1)86%  (91/106)89%  (22.4/25)
createItem (String): void 100% (1/1)87%  (66/76)86%  (12.9/15)
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)
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)
updateListLabel (int, String): void 100% (1/1)92%  (60/65)95%  (12.4/13)
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    public ListDesc fetchList(int listId)
172    {
173        ListDesc rv = null;
174        
175        synchronized (viewerLock)
176        {
177            for(ListDesc list : listData)
178            {
179                if(listId == list.getId())
180                {
181                    rv = list;
182                    break;
183                }
184            }
185        }
186 
187        return rv;
188    }
189 
190    
191    @Override
192    public void fetchItems(int listId)
193    {
194        Log.v(L.TAG, "CachingDataViewer.fetchItems(): Entered");
195        ViewerTask task = new ViewerTask();
196        task.taskId = ViewerTask.FETCH_ITEMS;
197        task.args = new Object[]{listId};
198        doTaskAndWait(task);
199        Log.v(L.TAG, "CachingDataViewer.fetchItems(): Exited");
200    }
201    
202 
203    @Override
204    public void createItem(String label)
205    {
206        if(selectedList == null)
207        {
208            Log.e(L.TAG, "CachingDataViewer.createItem(): called but no list is selected");
209            return;
210        }
211        
212        int listId = selectedList.getId();
213        
214        ItemDesc newItem = new ItemDesc(-1, label, true, false);
215        
216        ViewerTask createTask = new ViewerTask();
217        createTask.taskId = ViewerTask.CREATE_ITEM;
218        createTask.args = new Object[]{listId, label, newItem};
219        
220        synchronized (viewerLock)
221        {
222            // queue fetch lists
223            taskQueue.add(createTask);
224            viewerLock.notifyAll();
225            
226            itemData.add(0, newItem);
227            
228            updateListStats();
229        }
230    }
231 
232 
233    @Override
234    public void createList(String label)
235    {
236        ViewerTask createTask = new ViewerTask();
237        createTask.taskId = ViewerTask.CREATE_LIST;
238        createTask.args = new Object[]{label};
239 
240        ViewerTask fetchTask = new ViewerTask();
241        fetchTask.taskId = ViewerTask.FETCH_LISTS;
242        
243        synchronized (viewerLock)
244        {
245            // queue fetch lists
246            taskQueue.add(createTask);
247            taskQueue.add(fetchTask);
248            viewerLock.notifyAll();
249            
250            flushTasksNoLock();
251        }
252    }
253 
254 
255    @Override
256    public void deleteInactive()
257    {
258        if(selectedList == null)
259        {
260            Log.e(L.TAG, "CachingDataViewer.deleteInactive() called but no list is selected");
261            return;
262        }
263        
264        ViewerTask task = new ViewerTask();
265        task.taskId = ViewerTask.DELETE_INACTIVE;
266        task.args = new Object[]{selectedList.getId()};
267 
268        synchronized (viewerLock)
269        {
270            // queue fetch lists
271            taskQueue.add(task);
272            viewerLock.notifyAll();
273            
274            // update items data
275            List<ItemDesc> toDelete = new ArrayList<ItemDesc>();
276            for(ItemDesc i : itemData)
277            {
278                if(!i.isActive())
279                {
280                    toDelete.add(i);
281                }
282            }
283            itemData.removeAll(toDelete);
284            
285            updateListStats();
286        }
287    }
288 
289 
290    @Override
291    public void deleteItem(ItemDesc item)
292    {
293        itemIdBarrier(item);
294        
295        int itemId = item.getId();        
296        
297        ViewerTask task = new ViewerTask();
298        task.taskId = ViewerTask.DELETE_ITEM;
299        task.args = new Object[]{itemId};
300        
301        synchronized (viewerLock)
302        {
303            // queue fetch lists
304            taskQueue.add(task);
305            viewerLock.notifyAll();
306            
307            // update items data
308            itemData.remove(item);
309            updateListStats();
310        }
311    }
312 
313 
314    @Override
315    public void deleteList(int listId)
316    {
317        ViewerTask task = new ViewerTask();
318        task.taskId = ViewerTask.DELETE_LIST;
319        task.args = new Object[]{listId};
320        
321        synchronized (viewerLock)
322        {
323            // queue fetch lists
324            taskQueue.add(task);
325            viewerLock.notifyAll();
326            
327            // update lists data
328            ListDesc delete = null;
329            for(ListDesc list : listData)
330            {
331                if(listId == list.getId())
332                {
333                    delete = list;
334                    break;
335                }
336            }
337            listData.remove(delete);
338        }
339        
340    }
341 
342 
343    @Override
344    public void updateItemLabel(ItemDesc item, String newLabel)
345    {
346        itemIdBarrier(item);
347        int itemId = item.getId();
348        
349        ViewerTask task = new ViewerTask();
350        task.taskId = ViewerTask.UPDATE_ITEM_LABEL;
351        task.args = new Object[]{itemId, newLabel};
352        
353        synchronized (viewerLock)
354        {
355            // queue fetch lists
356            taskQueue.add(task);
357            viewerLock.notifyAll();
358            
359            // update items data
360            item.setLabel(newLabel);
361        }        
362    }
363 
364 
365    @Override
366    public void moveItem(ItemDesc item, int toListId)
367    {
368        itemIdBarrier(item);
369        int itemId = item.getId();
370 
371        ViewerTask task = new ViewerTask();
372        task.taskId = ViewerTask.MOVE_ITEM;
373        task.args = new Object[]{itemId, toListId};
374        
375        synchronized (viewerLock)
376        {
377            // queue fetch lists
378            taskQueue.add(task);
379            viewerLock.notifyAll();
380            
381            // remove from items data
382            ItemDesc itemInList = null;
383            for(ItemDesc i : itemData)
384            {
385                if(itemId == i.getId())
386                {
387                    itemInList = i;
388                    break;
389                }
390            }
391            if(itemInList != null)
392            {
393                itemData.remove(itemInList);
394                updateListStats();
395                ListDesc toList = findList(toListId);
396                toList.setTotalItems(toList.getTotalItems() + 1);
397                if(itemInList.isActive())
398                {
399                    toList.setActiveItems(toList.getActiveItems() + 1);
400                }
401            }
402            else
403            {
404                Log.w(L.TAG, "CachingDataViewer.moveItem(): Didn't find item in current item data");
405            }
406        }        
407    }
408 
409 
410    @Override
411    public void updateListLabel(int listId, String newLabel)
412    {
413        ViewerTask task = new ViewerTask();
414        task.taskId = ViewerTask.UPDATE_LIST_LABEL;
415        task.args = new Object[]{listId, newLabel};
416        
417        synchronized (viewerLock)
418        {
419            // queue fetch lists
420            taskQueue.add(task);
421            viewerLock.notifyAll();
422            
423            // update items data
424            for(ListDesc i : listData)
425            {
426                if(listId == i.getId())
427                {
428                    i.setLabel(newLabel);
429                    break;
430                }
431            }
432        }        
433    }
434    
435 
436    @Override
437    public void updateItemActiveness(ItemDesc item, boolean active)
438    {
439        itemIdBarrier(item);
440        int itemId = item.getId();
441        
442        ViewerTask task = new ViewerTask();
443        task.taskId = ViewerTask.UPDATE_ACTIVENESS;
444        task.args = new Object[]{itemId, active};
445        
446        synchronized (viewerLock)
447        {
448            // queue fetch lists
449            taskQueue.add(task);
450            viewerLock.notifyAll();
451            
452            // update items data
453            item.setActive(active);
454            
455            // update lists data
456            if(selectedList != null)
457            {
458                int activeItems = selectedList.getActiveItems();
459                activeItems += active?1:-1;
460                selectedList.setActiveItems(activeItems);
461            }
462        }
463    }
464    
465 
466    @Override
467    public void updateItemStarness(ItemDesc item, boolean star)
468    {
469        itemIdBarrier(item);
470        int itemId = item.getId();
471        
472        ViewerTask task = new ViewerTask();
473        task.taskId = ViewerTask.UPDATE_STARNESS;
474        task.args = new Object[]{itemId, star};
475        
476        synchronized (viewerLock)
477        {
478            // queue fetch lists
479            taskQueue.add(task);
480            viewerLock.notifyAll();
481            
482            // update items data
483            item.setStar(star);
484        }
485    }
486    
487    
488    private void itemIdBarrier(ItemDesc item)
489    {
490        if(item.getId() == -1)
491        {
492            Log.v(L.TAG, "CachingDataViewer.itemIdBarrier(): Used");
493            flushTasks();
494            
495            if(item.getId() == -1)
496            {
497                Log.e(L.TAG, "CachingDataViewer.itemIdBarrier(): failed!");
498            }
499        }
500    }
501    
502    
503    private void flushTasksNoLock()
504    {
505        while(taskQueue.size() != 0)
506        {
507            try
508            {
509                viewerLock.wait(200);
510            }
511            catch (InterruptedException e)
512            {
513                Log.e(L.TAG, "CachingDataViewer.flushTasksNoLock(): Exception waiting for flushTasksNoLock()", e);
514            }
515        }
516    }
517    
518    private void flushTasks()
519    {
520        synchronized (viewerLock)
521        {
522            flushTasksNoLock();
523        }
524    }
525    
526    private void updateListStats()
527    {
528        updateListStats(selectedList);
529    }
530    
531    private ListDesc findList(int listId)
532    {
533        ListDesc rvList = null;
534        for(ListDesc list : listData)
535        {
536            if(list.getId() == listId)
537            {
538                rvList = list;
539                break;
540            }
541        }
542 
543        return rvList;
544    }
545    
546    
547    private void updateListStats(ListDesc listDesc)
548    {
549        listDesc.setTotalItems(itemData.size());
550        int active = 0;
551        for(ItemDesc item : itemData)
552        {
553            if(item.isActive())
554            {
555                active++;
556            }
557        }
558        listDesc.setActiveItems(active);
559    }
560    
561    
562    private void doTaskAndWait(ViewerTask task)
563    {
564        synchronized (viewerLock)
565        {
566            taskQueue.add(task);
567            viewerLock.notifyAll();
568            
569            try
570            {
571                while(!task.done)
572                {
573                    viewerLock.wait();
574                }
575            }
576            catch(InterruptedException e)
577            {
578                Log.e(L.TAG, "CachingDataViewer.doTaskAndWait(): Error waiting for task", e);
579            }
580        }
581    }
582    
583    
584    private void dbUpdateLoop()
585    {
586        Log.v(L.TAG, "CachingDataViewer.dbUpdateLoop(): Entered");
587        while(running)
588        {
589            try
590            {
591                // get task
592                ViewerTask task;
593                synchronized (viewerLock)
594                {
595                    while(taskQueue.size() == 0)
596                    {
597                        interruptRequire = true;
598                        viewerLock.wait();
599                        interruptRequire = false;
600                    }
601                    task = taskQueue.peek();
602                }
603                
604                boolean doNotify = true;
605                try
606                {
607                    // do it
608                    switch(task.taskId)
609                    {
610                    case ViewerTask.FETCH_LISTS:
611                    {
612                        List<ListDesc> lists = dataManager.fetchLists();
613                        synchronized (viewerLock)
614                        {
615                            listData = lists;
616                            task.done = true;
617                            viewerLock.notifyAll();
618                        }
619                        doNotify = false;
620                        break;
621                    }
622                    case ViewerTask.FETCH_ITEMS:
623                    {
624                        synchronized (viewerLock)
625                        {
626                            itemData.clear();
627                            dataManager.fetchItems((Integer)task.args[0], itemData);
628                            task.done = true;
629                            viewerLock.notifyAll();
630                        }
631                        doNotify = false;
632                        break;
633                    }
634                    case ViewerTask.UPDATE_ACTIVENESS:
635                    {
636                        dataManager.updateItemActiveness((Integer)task.args[0], (Boolean)task.args[1]);
637                        task.done = true;
638                        break;
639                    }
640                    case ViewerTask.UPDATE_STARNESS:
641                    {
642                        dataManager.updateItemStarness((Integer)task.args[0], (Boolean)task.args[1]);
643                        task.done = true;
644                        break;
645                    }
646                    case ViewerTask.UPDATE_ITEM_LABEL:
647                    {
648                        dataManager.updateItemLabel((Integer)task.args[0], (String)task.args[1]);
649                        task.done = true;
650                        break;
651                    }
652                    case ViewerTask.UPDATE_LIST_LABEL:
653                    {
654                        dataManager.updateListLabel((Integer)task.args[0], (String)task.args[1]);
655                        task.done = true;
656                        break;
657                    }
658                    case ViewerTask.MOVE_ITEM:
659                    {
660                        dataManager.moveItem((Integer)task.args[0], (Integer)task.args[1]);
661                        task.done = true;
662                        break;
663                    }
664                    case ViewerTask.DELETE_LIST:
665                    {
666                        dataManager.deleteList((Integer)task.args[0]);
667                        task.done = true;
668                        break;
669                    }
670                    case ViewerTask.DELETE_ITEM:
671                    {
672                        dataManager.deleteItem((Integer)task.args[0]);
673                        task.done = true;
674                        break;
675                    }
676                    case ViewerTask.DELETE_INACTIVE:
677                    {
678                        dataManager.deleteInactive((Integer)task.args[0]);
679                        task.done = true;
680                        break;
681                    }
682                    case ViewerTask.CREATE_LIST:
683                    {
684                        dataManager.createList((String)task.args[0]);
685                        task.done = true;
686                        break;
687                    }
688                    case ViewerTask.CREATE_ITEM:
689                    {
690                        ItemDesc item = (ItemDesc)task.args[2];
691                        int id = dataManager.createItem((Integer)task.args[0], (String)task.args[1]);
692                        item.setId(id);
693                        task.done = true;
694                        break;
695                    }
696                    default:
697                        Log.w(L.TAG, "CachingDataViewer.dbUpdateLoop(): Unknown task enumeration " + task.taskId);
698                    }
699                }
700                finally
701                {
702                    synchronized (viewerLock)
703                    {
704                        taskQueue.remove();                
705                        if(doNotify)
706                        {
707                            viewerLock.notifyAll();
708                        }
709                    }
710                }                
711            }
712            catch(InterruptedException e)
713            {
714                if(running)
715                {
716                    Log.e(L.TAG, "CachingDataViewer.dbUpdateLoop(): Interrupted in DB update loop", e);
717                }
718                else
719                {
720                    Log.d(L.TAG, "CachingDataViewer.dbUpdateLoop(): interrupt exit");
721                }
722            }
723            catch(Exception e)
724            {
725                Log.e(L.TAG, "CachingDataViewer.dbUpdateLoop(): Exception in DB update loop", e);
726            }
727        }
728        Log.v(L.TAG, "CachingDataViewer.dbUpdateLoop(): Exit");
729    }
730    
731    
732    private static class ViewerTask
733    {
734        private static final int FETCH_LISTS = 0;
735        private static final int FETCH_ITEMS = 1;
736        private static final int UPDATE_ACTIVENESS = 2;
737        private static final int UPDATE_ITEM_LABEL = 3;
738        private static final int UPDATE_LIST_LABEL = 4;
739        private static final int DELETE_LIST = 5;
740        private static final int DELETE_ITEM = 6;
741        private static final int DELETE_INACTIVE = 7;
742        private static final int CREATE_LIST = 8;
743        private static final int CREATE_ITEM = 9;
744        private static final int UPDATE_STARNESS = 10;
745        private static final int MOVE_ITEM = 11;
746        
747        private int taskId;
748        private Object[] args;
749        private boolean done = false;
750    }
751 
752}

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