Home | History | Annotate | Download | only in event
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      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 
     17 package com.android.ddmuilib.log.event;
     18 
     19 import com.android.ddmlib.log.EventContainer;
     20 import com.android.ddmlib.log.EventLogParser;
     21 import com.android.ddmlib.log.InvalidTypeException;
     22 import org.eclipse.swt.widgets.Composite;
     23 import org.eclipse.swt.widgets.Control;
     24 import org.jfree.chart.labels.CustomXYToolTipGenerator;
     25 import org.jfree.chart.plot.XYPlot;
     26 import org.jfree.chart.renderer.xy.XYBarRenderer;
     27 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
     28 import org.jfree.data.time.FixedMillisecond;
     29 import org.jfree.data.time.SimpleTimePeriod;
     30 import org.jfree.data.time.TimePeriodValues;
     31 import org.jfree.data.time.TimePeriodValuesCollection;
     32 import org.jfree.data.time.TimeSeries;
     33 import org.jfree.data.time.TimeSeriesCollection;
     34 import org.jfree.util.ShapeUtilities;
     35 
     36 import java.awt.Color;
     37 import java.util.ArrayList;
     38 import java.util.List;
     39 import java.util.Scanner;
     40 import java.util.regex.Pattern;
     41 
     42 public class DisplaySync extends SyncCommon {
     43 
     44     // Information to graph for each authority
     45     private TimePeriodValues mDatasetsSync[];
     46     private List<String> mTooltipsSync[];
     47     private CustomXYToolTipGenerator mTooltipGenerators[];
     48     private TimeSeries mDatasetsSyncTickle[];
     49 
     50     // Dataset of error events to graph
     51     private TimeSeries mDatasetError;
     52 
     53     public DisplaySync(String name) {
     54         super(name);
     55     }
     56 
     57     /**
     58      * Creates the UI for the event display.
     59      * @param parent the parent composite.
     60      * @param logParser the current log parser.
     61      * @return the created control (which may have children).
     62      */
     63     @Override
     64     public Control createComposite(final Composite parent, EventLogParser logParser,
     65             final ILogColumnListener listener) {
     66         Control composite = createCompositeChart(parent, logParser, "Sync Status");
     67         resetUI();
     68         return composite;
     69     }
     70 
     71     /**
     72      * Resets the display.
     73      */
     74     @Override
     75     void resetUI() {
     76         super.resetUI();
     77         XYPlot xyPlot = mChart.getXYPlot();
     78 
     79         XYBarRenderer br = new XYBarRenderer();
     80         mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
     81         mTooltipsSync = new List[NUM_AUTHS];
     82         mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
     83 
     84         TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
     85         xyPlot.setDataset(tpvc);
     86         xyPlot.setRenderer(0, br);
     87 
     88         XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
     89         ls.setBaseLinesVisible(false);
     90         mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
     91         TimeSeriesCollection tsc = new TimeSeriesCollection();
     92         xyPlot.setDataset(1, tsc);
     93         xyPlot.setRenderer(1, ls);
     94 
     95         mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
     96         xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
     97         XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
     98         errls.setBaseLinesVisible(false);
     99         errls.setSeriesPaint(0, Color.RED);
    100         xyPlot.setRenderer(2, errls);
    101 
    102         for (int i = 0; i < NUM_AUTHS; i++) {
    103             br.setSeriesPaint(i, AUTH_COLORS[i]);
    104             ls.setSeriesPaint(i, AUTH_COLORS[i]);
    105             mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
    106             tpvc.addSeries(mDatasetsSync[i]);
    107             mTooltipsSync[i] = new ArrayList<String>();
    108             mTooltipGenerators[i] = new CustomXYToolTipGenerator();
    109             br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
    110             mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
    111 
    112             mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle",
    113                     FixedMillisecond.class);
    114             tsc.addSeries(mDatasetsSyncTickle[i]);
    115             ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
    116         }
    117     }
    118 
    119     /**
    120      * Updates the display with a new event.
    121      *
    122      * @param event     The event
    123      * @param logParser The parser providing the event.
    124      */
    125     @Override
    126     void newEvent(EventContainer event, EventLogParser logParser) {
    127         super.newEvent(event, logParser); // Handle sync operation
    128         try {
    129             if (event.mTag == EVENT_TICKLE) {
    130                 int auth = getAuth(event.getValueAsString(0));
    131                 if (auth >= 0) {
    132                     long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
    133                     mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
    134                 }
    135             }
    136         } catch (InvalidTypeException e) {
    137         }
    138     }
    139 
    140     /**
    141      * Generate the height for an event.
    142      * Height is somewhat arbitrarily the count of "things" that happened
    143      * during the sync.
    144      * When network traffic measurements are available, code should be modified
    145      * to use that instead.
    146      * @param details The details string associated with the event
    147      * @return The height in arbirary units (0-100)
    148      */
    149     private int getHeightFromDetails(String details) {
    150         if (details == null) {
    151             return 1; // Arbitrary
    152         }
    153         int total = 0;
    154         String parts[] = details.split("[a-zA-Z]");
    155         for (String part : parts) {
    156             if ("".equals(part)) continue;
    157             total += Integer.parseInt(part);
    158         }
    159         if (total == 0) {
    160             total = 1;
    161         }
    162         return total;
    163     }
    164 
    165     /**
    166      * Generates the tooltips text for an event.
    167      * This method decodes the cryptic details string.
    168      * @param auth The authority associated with the event
    169      * @param details The details string
    170      * @param eventSource server, poll, etc.
    171      * @return The text to display in the tooltips
    172      */
    173     private String getTextFromDetails(int auth, String details, int eventSource) {
    174 
    175         StringBuffer sb = new StringBuffer();
    176         sb.append(AUTH_NAMES[auth]).append(": \n");
    177 
    178         Scanner scanner = new Scanner(details);
    179         Pattern charPat = Pattern.compile("[a-zA-Z]");
    180         Pattern numPat = Pattern.compile("[0-9]+");
    181         while (scanner.hasNext()) {
    182             String key = scanner.findInLine(charPat);
    183             int val = Integer.parseInt(scanner.findInLine(numPat));
    184             if (auth == GMAIL && "M".equals(key)) {
    185                 sb.append("messages from server: ").append(val).append("\n");
    186             } else if (auth == GMAIL && "L".equals(key)) {
    187                 sb.append("labels from server: ").append(val).append("\n");
    188             } else if (auth == GMAIL && "C".equals(key)) {
    189                 sb.append("check conversation requests from server: ").append(val).append("\n");
    190             } else if (auth == GMAIL && "A".equals(key)) {
    191                 sb.append("attachments from server: ").append(val).append("\n");
    192             } else if (auth == GMAIL && "U".equals(key)) {
    193                 sb.append("op updates from server: ").append(val).append("\n");
    194             } else if (auth == GMAIL && "u".equals(key)) {
    195                 sb.append("op updates to server: ").append(val).append("\n");
    196             } else if (auth == GMAIL && "S".equals(key)) {
    197                 sb.append("send/receive cycles: ").append(val).append("\n");
    198             } else if ("Q".equals(key)) {
    199                 sb.append("queries to server: ").append(val).append("\n");
    200             } else if ("E".equals(key)) {
    201                 sb.append("entries from server: ").append(val).append("\n");
    202             } else if ("u".equals(key)) {
    203                 sb.append("updates from client: ").append(val).append("\n");
    204             } else if ("i".equals(key)) {
    205                 sb.append("inserts from client: ").append(val).append("\n");
    206             } else if ("d".equals(key)) {
    207                 sb.append("deletes from client: ").append(val).append("\n");
    208             } else if ("f".equals(key)) {
    209                 sb.append("full sync requested\n");
    210             } else if ("r".equals(key)) {
    211                 sb.append("partial sync unavailable\n");
    212             } else if ("X".equals(key)) {
    213                 sb.append("hard error\n");
    214             } else if ("e".equals(key)) {
    215                 sb.append("number of parse exceptions: ").append(val).append("\n");
    216             } else if ("c".equals(key)) {
    217                 sb.append("number of conflicts: ").append(val).append("\n");
    218             } else if ("a".equals(key)) {
    219                 sb.append("number of auth exceptions: ").append(val).append("\n");
    220             } else if ("D".equals(key)) {
    221                 sb.append("too many deletions\n");
    222             } else if ("R".equals(key)) {
    223                 sb.append("too many retries: ").append(val).append("\n");
    224             } else if ("b".equals(key)) {
    225                 sb.append("database error\n");
    226             } else if ("x".equals(key)) {
    227                 sb.append("soft error\n");
    228             } else if ("l".equals(key)) {
    229                 sb.append("sync already in progress\n");
    230             } else if ("I".equals(key)) {
    231                 sb.append("io exception\n");
    232             } else if (auth == CONTACTS && "g".equals(key)) {
    233                 sb.append("aggregation query: ").append(val).append("\n");
    234             } else if (auth == CONTACTS && "G".equals(key)) {
    235                 sb.append("aggregation merge: ").append(val).append("\n");
    236             } else if (auth == CONTACTS && "n".equals(key)) {
    237                 sb.append("num entries: ").append(val).append("\n");
    238             } else if (auth == CONTACTS && "p".equals(key)) {
    239                 sb.append("photos uploaded from server: ").append(val).append("\n");
    240             } else if (auth == CONTACTS && "P".equals(key)) {
    241                 sb.append("photos downloaded from server: ").append(val).append("\n");
    242             } else if (auth == CALENDAR && "F".equals(key)) {
    243                 sb.append("server refresh\n");
    244             } else if (auth == CALENDAR && "s".equals(key)) {
    245                 sb.append("server diffs fetched\n");
    246             } else {
    247                 sb.append(key).append("=").append(val);
    248             }
    249         }
    250         if (eventSource == 0) {
    251             sb.append("(server)");
    252         } else if (eventSource == 1) {
    253             sb.append("(local)");
    254         } else if (eventSource == 2) {
    255             sb.append("(poll)");
    256         } else if (eventSource == 3) {
    257             sb.append("(user)");
    258         }
    259         return sb.toString();
    260     }
    261 
    262 
    263     /**
    264      * Callback to process a sync event.
    265      */
    266     @Override
    267     void processSyncEvent(EventContainer event, int auth, long startTime, long stopTime,
    268             String details, boolean newEvent, int syncSource) {
    269         if (!newEvent) {
    270             // Details arrived for a previous sync event
    271             // Remove event before reinserting.
    272             int lastItem = mDatasetsSync[auth].getItemCount();
    273             mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
    274             mTooltipsSync[auth].remove(lastItem-1);
    275         }
    276         double height = getHeightFromDetails(details);
    277         height = height / (stopTime - startTime + 1) * 10000;
    278         if (height > 30) {
    279             height = 30;
    280         }
    281         mDatasetsSync[auth].add(new SimpleTimePeriod(startTime, stopTime), height);
    282         mTooltipsSync[auth].add(getTextFromDetails(auth, details, syncSource));
    283         mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
    284         if (details.indexOf('x') >= 0 || details.indexOf('X') >= 0) {
    285             long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
    286             mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
    287         }
    288     }
    289 
    290     /**
    291      * Gets display type
    292      *
    293      * @return display type as an integer
    294      */
    295     @Override
    296     int getDisplayType() {
    297         return DISPLAY_TYPE_SYNC;
    298     }
    299 }
    300