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;
     20 import com.android.ddmlib.log.EventContainer;
     21 import com.android.ddmlib.log.EventContainer.CompareMethod;
     22 import com.android.ddmlib.log.EventContainer.EventValueType;
     23 import com.android.ddmlib.log.EventLogParser;
     24 import com.android.ddmlib.log.EventValueDescription.ValueType;
     25 import com.android.ddmlib.log.InvalidTypeException;
     26 
     27 import org.eclipse.swt.SWT;
     28 import org.eclipse.swt.events.DisposeEvent;
     29 import org.eclipse.swt.events.DisposeListener;
     30 import org.eclipse.swt.graphics.Font;
     31 import org.eclipse.swt.graphics.FontData;
     32 import org.eclipse.swt.widgets.Composite;
     33 import org.eclipse.swt.widgets.Control;
     34 import org.eclipse.swt.widgets.Table;
     35 import org.eclipse.swt.widgets.TableColumn;
     36 import org.jfree.chart.ChartFactory;
     37 import org.jfree.chart.JFreeChart;
     38 import org.jfree.chart.event.ChartChangeEvent;
     39 import org.jfree.chart.event.ChartChangeEventType;
     40 import org.jfree.chart.event.ChartChangeListener;
     41 import org.jfree.chart.plot.XYPlot;
     42 import org.jfree.chart.title.TextTitle;
     43 import org.jfree.data.time.Millisecond;
     44 import org.jfree.data.time.TimeSeries;
     45 import org.jfree.data.time.TimeSeriesCollection;
     46 import org.jfree.experimental.chart.swt.ChartComposite;
     47 import org.jfree.experimental.swt.SWTUtils;
     48 
     49 import java.security.InvalidParameterException;
     50 import java.util.ArrayList;
     51 import java.util.Date;
     52 import java.util.HashMap;
     53 import java.util.Iterator;
     54 import java.util.Set;
     55 import java.util.regex.Pattern;
     56 
     57 /**
     58  * Represents a custom display of one or more events.
     59  */
     60 abstract class EventDisplay {
     61 
     62     private final static String DISPLAY_DATA_STORAGE_SEPARATOR = ":"; //$NON-NLS-1$
     63     private final static String PID_STORAGE_SEPARATOR = ","; //$NON-NLS-1$
     64     private final static String DESCRIPTOR_STORAGE_SEPARATOR = "$"; //$NON-NLS-1$
     65     private final static String DESCRIPTOR_DATA_STORAGE_SEPARATOR = "!"; //$NON-NLS-1$
     66 
     67     private final static String FILTER_VALUE_NULL = "<null>"; //$NON-NLS-1$
     68 
     69     public final static int DISPLAY_TYPE_LOG_ALL = 0;
     70     public final static int DISPLAY_TYPE_FILTERED_LOG = 1;
     71     public final static int DISPLAY_TYPE_GRAPH = 2;
     72     public final static int DISPLAY_TYPE_SYNC = 3;
     73     public final static int DISPLAY_TYPE_SYNC_HIST = 4;
     74     public final static int DISPLAY_TYPE_SYNC_PERF = 5;
     75 
     76     private final static int EVENT_CHECK_FAILED = 0;
     77     protected final static int EVENT_CHECK_SAME_TAG = 1;
     78     protected final static int EVENT_CHECK_SAME_VALUE = 2;
     79 
     80     /**
     81      * Creates the appropriate EventDisplay subclass.
     82      *
     83      * @param type the type of display (DISPLAY_TYPE_LOG_ALL, etc)
     84      * @param name the name of the display
     85      * @return the created object
     86      */
     87     public static EventDisplay eventDisplayFactory(int type, String name) {
     88         switch (type) {
     89             case DISPLAY_TYPE_LOG_ALL:
     90                 return new DisplayLog(name);
     91             case DISPLAY_TYPE_FILTERED_LOG:
     92                 return new DisplayFilteredLog(name);
     93             case DISPLAY_TYPE_SYNC:
     94                 return new DisplaySync(name);
     95             case DISPLAY_TYPE_SYNC_HIST:
     96                 return new DisplaySyncHistogram(name);
     97             case DISPLAY_TYPE_GRAPH:
     98                 return new DisplayGraph(name);
     99             case DISPLAY_TYPE_SYNC_PERF:
    100                 return new DisplaySyncPerf(name);
    101             default:
    102                 throw new InvalidParameterException("Unknown Display Type " + type); //$NON-NLS-1$
    103         }
    104     }
    105 
    106     /**
    107      * Adds event to the display.
    108      * @param event The event
    109      * @param logParser The log parser.
    110      */
    111     abstract void newEvent(EventContainer event, EventLogParser logParser);
    112 
    113     /**
    114      * Resets the display.
    115      */
    116     abstract void resetUI();
    117 
    118     /**
    119      * Gets display type
    120      *
    121      * @return display type as an integer
    122      */
    123     abstract int getDisplayType();
    124 
    125     /**
    126      * Creates the UI for the event display.
    127      *
    128      * @param parent    the parent composite.
    129      * @param logParser the current log parser.
    130      * @return the created control (which may have children).
    131      */
    132     abstract Control createComposite(final Composite parent, EventLogParser logParser,
    133             final ILogColumnListener listener);
    134 
    135     interface ILogColumnListener {
    136         void columnResized(int index, TableColumn sourceColumn);
    137     }
    138 
    139     /**
    140      * Describes an event to be displayed.
    141      */
    142     static class OccurrenceDisplayDescriptor {
    143 
    144         int eventTag = -1;
    145         int seriesValueIndex = -1;
    146         boolean includePid = false;
    147         int filterValueIndex = -1;
    148         CompareMethod filterCompareMethod = CompareMethod.EQUAL_TO;
    149         Object filterValue = null;
    150 
    151         OccurrenceDisplayDescriptor() {
    152         }
    153 
    154         OccurrenceDisplayDescriptor(OccurrenceDisplayDescriptor descriptor) {
    155             replaceWith(descriptor);
    156         }
    157 
    158         OccurrenceDisplayDescriptor(int eventTag) {
    159             this.eventTag = eventTag;
    160         }
    161 
    162         OccurrenceDisplayDescriptor(int eventTag, int seriesValueIndex) {
    163             this.eventTag = eventTag;
    164             this.seriesValueIndex = seriesValueIndex;
    165         }
    166 
    167         void replaceWith(OccurrenceDisplayDescriptor descriptor) {
    168             eventTag = descriptor.eventTag;
    169             seriesValueIndex = descriptor.seriesValueIndex;
    170             includePid = descriptor.includePid;
    171             filterValueIndex = descriptor.filterValueIndex;
    172             filterCompareMethod = descriptor.filterCompareMethod;
    173             filterValue = descriptor.filterValue;
    174         }
    175 
    176         /**
    177          * Loads the descriptor parameter from a storage string. The storage string must have
    178          * been generated with {@link #getStorageString()}.
    179          *
    180          * @param storageString the storage string
    181          */
    182         final void loadFrom(String storageString) {
    183             String[] values = storageString.split(Pattern.quote(DESCRIPTOR_DATA_STORAGE_SEPARATOR));
    184             loadFrom(values, 0);
    185         }
    186 
    187         /**
    188          * Loads the parameters from an array of strings.
    189          *
    190          * @param storageStrings the strings representing each parameter.
    191          * @param index          the starting index in the array of strings.
    192          * @return the new index in the array.
    193          */
    194         protected int loadFrom(String[] storageStrings, int index) {
    195             eventTag = Integer.parseInt(storageStrings[index++]);
    196             seriesValueIndex = Integer.parseInt(storageStrings[index++]);
    197             includePid = Boolean.parseBoolean(storageStrings[index++]);
    198             filterValueIndex = Integer.parseInt(storageStrings[index++]);
    199             try {
    200                 filterCompareMethod = CompareMethod.valueOf(storageStrings[index++]);
    201             } catch (IllegalArgumentException e) {
    202                 // if the name does not match any known CompareMethod, we init it to the default one
    203                 filterCompareMethod = CompareMethod.EQUAL_TO;
    204             }
    205             String value = storageStrings[index++];
    206             if (filterValueIndex != -1 && FILTER_VALUE_NULL.equals(value) == false) {
    207                 filterValue = EventValueType.getObjectFromStorageString(value);
    208             }
    209 
    210             return index;
    211         }
    212 
    213         /**
    214          * Returns the storage string for the receiver.
    215          */
    216         String getStorageString() {
    217             StringBuilder sb = new StringBuilder();
    218             sb.append(eventTag);
    219             sb.append(DESCRIPTOR_DATA_STORAGE_SEPARATOR);
    220             sb.append(seriesValueIndex);
    221             sb.append(DESCRIPTOR_DATA_STORAGE_SEPARATOR);
    222             sb.append(Boolean.toString(includePid));
    223             sb.append(DESCRIPTOR_DATA_STORAGE_SEPARATOR);
    224             sb.append(filterValueIndex);
    225             sb.append(DESCRIPTOR_DATA_STORAGE_SEPARATOR);
    226             sb.append(filterCompareMethod.name());
    227             sb.append(DESCRIPTOR_DATA_STORAGE_SEPARATOR);
    228             if (filterValue != null) {
    229                 String value = EventValueType.getStorageString(filterValue);
    230                 if (value != null) {
    231                     sb.append(value);
    232                 } else {
    233                     sb.append(FILTER_VALUE_NULL);
    234                 }
    235             } else {
    236                 sb.append(FILTER_VALUE_NULL);
    237             }
    238 
    239             return sb.toString();
    240         }
    241     }
    242 
    243     /**
    244      * Describes an event value to be displayed.
    245      */
    246     static final class ValueDisplayDescriptor extends OccurrenceDisplayDescriptor {
    247         String valueName;
    248         int valueIndex = -1;
    249 
    250         ValueDisplayDescriptor() {
    251             super();
    252         }
    253 
    254         ValueDisplayDescriptor(ValueDisplayDescriptor descriptor) {
    255             super();
    256             replaceWith(descriptor);
    257         }
    258 
    259         ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex) {
    260             super(eventTag);
    261             this.valueName = valueName;
    262             this.valueIndex = valueIndex;
    263         }
    264 
    265         ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex,
    266                 int seriesValueIndex) {
    267             super(eventTag, seriesValueIndex);
    268             this.valueName = valueName;
    269             this.valueIndex = valueIndex;
    270         }
    271 
    272         @Override
    273         void replaceWith(OccurrenceDisplayDescriptor descriptor) {
    274             super.replaceWith(descriptor);
    275             if (descriptor instanceof ValueDisplayDescriptor) {
    276                 ValueDisplayDescriptor valueDescriptor = (ValueDisplayDescriptor) descriptor;
    277                 valueName = valueDescriptor.valueName;
    278                 valueIndex = valueDescriptor.valueIndex;
    279             }
    280         }
    281 
    282         /**
    283          * Loads the parameters from an array of strings.
    284          *
    285          * @param storageStrings the strings representing each parameter.
    286          * @param index          the starting index in the array of strings.
    287          * @return the new index in the array.
    288          */
    289         @Override
    290         protected int loadFrom(String[] storageStrings, int index) {
    291             index = super.loadFrom(storageStrings, index);
    292             valueName = storageStrings[index++];
    293             valueIndex = Integer.parseInt(storageStrings[index++]);
    294             return index;
    295         }
    296 
    297         /**
    298          * Returns the storage string for the receiver.
    299          */
    300         @Override
    301         String getStorageString() {
    302             String superStorage = super.getStorageString();
    303 
    304             StringBuilder sb = new StringBuilder();
    305             sb.append(superStorage);
    306             sb.append(DESCRIPTOR_DATA_STORAGE_SEPARATOR);
    307             sb.append(valueName);
    308             sb.append(DESCRIPTOR_DATA_STORAGE_SEPARATOR);
    309             sb.append(valueIndex);
    310 
    311             return sb.toString();
    312         }
    313     }
    314 
    315     /* ==================
    316      * Event Display parameters.
    317      * ================== */
    318     protected String mName;
    319 
    320     private boolean mPidFiltering = false;
    321 
    322     private ArrayList<Integer> mPidFilterList = null;
    323 
    324     protected final ArrayList<ValueDisplayDescriptor> mValueDescriptors =
    325             new ArrayList<ValueDisplayDescriptor>();
    326     private final ArrayList<OccurrenceDisplayDescriptor> mOccurrenceDescriptors =
    327             new ArrayList<OccurrenceDisplayDescriptor>();
    328 
    329     /* ==================
    330      * Event Display members for display purpose.
    331      * ================== */
    332     // chart objects
    333     /**
    334      * This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series)
    335      */
    336     protected final HashMap<ValueDisplayDescriptor, HashMap<Integer, TimeSeries>> mValueDescriptorSeriesMap =
    337             new HashMap<ValueDisplayDescriptor, HashMap<Integer, TimeSeries>>();
    338     /**
    339      * This is a map of (descriptor, map2) where map2 is a map of (pid, chart-series)
    340      */
    341     protected final HashMap<OccurrenceDisplayDescriptor, HashMap<Integer, TimeSeries>> mOcurrenceDescriptorSeriesMap =
    342             new HashMap<OccurrenceDisplayDescriptor, HashMap<Integer, TimeSeries>>();
    343 
    344     /**
    345      * This is a map of (ValueType, dataset)
    346      */
    347     protected final HashMap<ValueType, TimeSeriesCollection> mValueTypeDataSetMap =
    348             new HashMap<ValueType, TimeSeriesCollection>();
    349 
    350     protected JFreeChart mChart;
    351     protected TimeSeriesCollection mOccurrenceDataSet;
    352     protected int mDataSetCount;
    353     private ChartComposite mChartComposite;
    354     protected long mMaximumChartItemAge = -1;
    355     protected long mHistWidth = 1;
    356 
    357     // log objects.
    358     protected Table mLogTable;
    359 
    360     /* ==================
    361      * Misc data.
    362      * ================== */
    363     protected int mValueDescriptorCheck = EVENT_CHECK_FAILED;
    364 
    365     EventDisplay(String name) {
    366         mName = name;
    367     }
    368 
    369     static EventDisplay clone(EventDisplay from) {
    370         EventDisplay ed = eventDisplayFactory(from.getDisplayType(), from.getName());
    371         ed.mName = from.mName;
    372         ed.mPidFiltering = from.mPidFiltering;
    373         ed.mMaximumChartItemAge = from.mMaximumChartItemAge;
    374         ed.mHistWidth = from.mHistWidth;
    375 
    376         if (from.mPidFilterList != null) {
    377             ed.mPidFilterList = new ArrayList<Integer>();
    378             ed.mPidFilterList.addAll(from.mPidFilterList);
    379         }
    380 
    381         for (ValueDisplayDescriptor desc : from.mValueDescriptors) {
    382             ed.mValueDescriptors.add(new ValueDisplayDescriptor(desc));
    383         }
    384         ed.mValueDescriptorCheck = from.mValueDescriptorCheck;
    385 
    386         for (OccurrenceDisplayDescriptor desc : from.mOccurrenceDescriptors) {
    387             ed.mOccurrenceDescriptors.add(new OccurrenceDisplayDescriptor(desc));
    388         }
    389         return ed;
    390     }
    391 
    392     /**
    393      * Returns the parameters of the receiver as a single String for storage.
    394      */
    395     String getStorageString() {
    396         StringBuilder sb = new StringBuilder();
    397 
    398         sb.append(mName);
    399         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    400         sb.append(getDisplayType());
    401         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    402         sb.append(Boolean.toString(mPidFiltering));
    403         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    404         sb.append(getPidStorageString());
    405         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    406         sb.append(getDescriptorStorageString(mValueDescriptors));
    407         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    408         sb.append(getDescriptorStorageString(mOccurrenceDescriptors));
    409         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    410         sb.append(mMaximumChartItemAge);
    411         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    412         sb.append(mHistWidth);
    413         sb.append(DISPLAY_DATA_STORAGE_SEPARATOR);
    414 
    415         return sb.toString();
    416     }
    417 
    418     void setName(String name) {
    419         mName = name;
    420     }
    421 
    422     String getName() {
    423         return mName;
    424     }
    425 
    426     void setPidFiltering(boolean filterByPid) {
    427         mPidFiltering = filterByPid;
    428     }
    429 
    430     boolean getPidFiltering() {
    431         return mPidFiltering;
    432     }
    433 
    434     void setPidFilterList(ArrayList<Integer> pids) {
    435         if (mPidFiltering == false) {
    436             throw new InvalidParameterException();
    437         }
    438 
    439         mPidFilterList = pids;
    440     }
    441 
    442     ArrayList<Integer> getPidFilterList() {
    443         return mPidFilterList;
    444     }
    445 
    446     void addPidFiler(int pid) {
    447         if (mPidFiltering == false) {
    448             throw new InvalidParameterException();
    449         }
    450 
    451         if (mPidFilterList == null) {
    452             mPidFilterList = new ArrayList<Integer>();
    453         }
    454 
    455         mPidFilterList.add(pid);
    456     }
    457 
    458     /**
    459      * Returns an iterator to the list of {@link ValueDisplayDescriptor}.
    460      */
    461     Iterator<ValueDisplayDescriptor> getValueDescriptors() {
    462         return mValueDescriptors.iterator();
    463     }
    464 
    465     /**
    466      * Update checks on the descriptors. Must be called whenever a descriptor is modified outside
    467      * of this class.
    468      */
    469     void updateValueDescriptorCheck() {
    470         mValueDescriptorCheck = checkDescriptors();
    471     }
    472 
    473     /**
    474      * Returns an iterator to the list of {@link OccurrenceDisplayDescriptor}.
    475      */
    476     Iterator<OccurrenceDisplayDescriptor> getOccurrenceDescriptors() {
    477         return mOccurrenceDescriptors.iterator();
    478     }
    479 
    480     /**
    481      * Adds a descriptor. This can be a {@link OccurrenceDisplayDescriptor} or a
    482      * {@link ValueDisplayDescriptor}.
    483      *
    484      * @param descriptor the descriptor to be added.
    485      */
    486     void addDescriptor(OccurrenceDisplayDescriptor descriptor) {
    487         if (descriptor instanceof ValueDisplayDescriptor) {
    488             mValueDescriptors.add((ValueDisplayDescriptor) descriptor);
    489             mValueDescriptorCheck = checkDescriptors();
    490         } else {
    491             mOccurrenceDescriptors.add(descriptor);
    492         }
    493     }
    494 
    495     /**
    496      * Returns a descriptor by index and class (extending {@link OccurrenceDisplayDescriptor}).
    497      *
    498      * @param descriptorClass the class of the descriptor to return.
    499      * @param index           the index of the descriptor to return.
    500      * @return either a {@link OccurrenceDisplayDescriptor} or a {@link ValueDisplayDescriptor}
    501      *         or <code>null</code> if <code>descriptorClass</code> is another class.
    502      */
    503     OccurrenceDisplayDescriptor getDescriptor(
    504             Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index) {
    505 
    506         if (descriptorClass == OccurrenceDisplayDescriptor.class) {
    507             return mOccurrenceDescriptors.get(index);
    508         } else if (descriptorClass == ValueDisplayDescriptor.class) {
    509             return mValueDescriptors.get(index);
    510         }
    511 
    512         return null;
    513     }
    514 
    515     /**
    516      * Removes a descriptor based on its class and index.
    517      *
    518      * @param descriptorClass the class of the descriptor.
    519      * @param index           the index of the descriptor to be removed.
    520      */
    521     void removeDescriptor(Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index) {
    522         if (descriptorClass == OccurrenceDisplayDescriptor.class) {
    523             mOccurrenceDescriptors.remove(index);
    524         } else if (descriptorClass == ValueDisplayDescriptor.class) {
    525             mValueDescriptors.remove(index);
    526             mValueDescriptorCheck = checkDescriptors();
    527         }
    528     }
    529 
    530     Control createCompositeChart(final Composite parent, EventLogParser logParser,
    531             String title) {
    532         mChart = ChartFactory.createTimeSeriesChart(
    533                 null,
    534                 null /* timeAxisLabel */,
    535                 null /* valueAxisLabel */,
    536                 null, /* dataset. set below */
    537                 true /* legend */,
    538                 false /* tooltips */,
    539                 false /* urls */);
    540 
    541         // get the font to make a proper title. We need to convert the swt font,
    542         // into an awt font.
    543         Font f = parent.getFont();
    544         FontData[] fData = f.getFontData();
    545 
    546         // event though on Mac OS there could be more than one fontData, we'll only use
    547         // the first one.
    548         FontData firstFontData = fData[0];
    549 
    550         java.awt.Font awtFont = SWTUtils.toAwtFont(parent.getDisplay(),
    551                 firstFontData, true /* ensureSameSize */);
    552 
    553 
    554         mChart.setTitle(new TextTitle(title, awtFont));
    555 
    556         final XYPlot xyPlot = mChart.getXYPlot();
    557         xyPlot.setRangeCrosshairVisible(true);
    558         xyPlot.setRangeCrosshairLockedOnData(true);
    559         xyPlot.setDomainCrosshairVisible(true);
    560         xyPlot.setDomainCrosshairLockedOnData(true);
    561 
    562         mChart.addChangeListener(new ChartChangeListener() {
    563             @Override
    564             public void chartChanged(ChartChangeEvent event) {
    565                 ChartChangeEventType type = event.getType();
    566                 if (type == ChartChangeEventType.GENERAL) {
    567                     // because the value we need (rangeCrosshair and domainCrosshair) are
    568                     // updated on the draw, but the notification happens before the draw,
    569                     // we process the click in a future runnable!
    570                     parent.getDisplay().asyncExec(new Runnable() {
    571                         @Override
    572                         public void run() {
    573                             processClick(xyPlot);
    574                         }
    575                     });
    576                 }
    577             }
    578         });
    579 
    580         mChartComposite = new ChartComposite(parent, SWT.BORDER, mChart,
    581                 ChartComposite.DEFAULT_WIDTH,
    582                 ChartComposite.DEFAULT_HEIGHT,
    583                 ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
    584                 ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
    585                 3000, // max draw width. We don't want it to zoom, so we put a big number
    586                 3000, // max draw height. We don't want it to zoom, so we put a big number
    587                 true,  // off-screen buffer
    588                 true,  // properties
    589                 true,  // save
    590                 true,  // print
    591                 true,  // zoom
    592                 true);   // tooltips
    593 
    594         mChartComposite.addDisposeListener(new DisposeListener() {
    595             @Override
    596             public void widgetDisposed(DisposeEvent e) {
    597                 mValueTypeDataSetMap.clear();
    598                 mDataSetCount = 0;
    599                 mOccurrenceDataSet = null;
    600                 mChart = null;
    601                 mChartComposite = null;
    602                 mValueDescriptorSeriesMap.clear();
    603                 mOcurrenceDescriptorSeriesMap.clear();
    604             }
    605         });
    606 
    607         return mChartComposite;
    608 
    609     }
    610 
    611     private void processClick(XYPlot xyPlot) {
    612         double rangeValue = xyPlot.getRangeCrosshairValue();
    613         if (rangeValue != 0) {
    614             double domainValue = xyPlot.getDomainCrosshairValue();
    615 
    616             Millisecond msec = new Millisecond(new Date((long) domainValue));
    617 
    618             // look for values in the dataset that contains data at this TimePeriod
    619             Set<ValueDisplayDescriptor> descKeys = mValueDescriptorSeriesMap.keySet();
    620 
    621             for (ValueDisplayDescriptor descKey : descKeys) {
    622                 HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descKey);
    623 
    624                 Set<Integer> pidKeys = map.keySet();
    625 
    626                 for (Integer pidKey : pidKeys) {
    627                     TimeSeries series = map.get(pidKey);
    628 
    629                     Number value = series.getValue(msec);
    630                     if (value != null) {
    631                         // found a match. lets check against the actual value.
    632                         if (value.doubleValue() == rangeValue) {
    633 
    634                             return;
    635                         }
    636                     }
    637                 }
    638             }
    639         }
    640     }
    641 
    642 
    643     /**
    644      * Resizes the <code>index</code>-th column of the log {@link Table} (if applicable).
    645      * Subclasses can override if necessary.
    646      * <p/>
    647      * This does nothing if the <code>Table</code> object is <code>null</code> (because the display
    648      * type does not use a column) or if the <code>index</code>-th column is in fact the originating
    649      * column passed as argument.
    650      *
    651      * @param index        the index of the column to resize
    652      * @param sourceColumn the original column that was resize, and on which we need to sync the
    653      *                     index-th column width.
    654      */
    655     void resizeColumn(int index, TableColumn sourceColumn) {
    656     }
    657 
    658     /**
    659      * Sets the current {@link EventLogParser} object.
    660      * Subclasses can override if necessary.
    661      */
    662     protected void setNewLogParser(EventLogParser logParser) {
    663     }
    664 
    665     /**
    666      * Prepares the {@link EventDisplay} for a multi event display.
    667      */
    668     void startMultiEventDisplay() {
    669         if (mLogTable != null) {
    670             mLogTable.setRedraw(false);
    671         }
    672     }
    673 
    674     /**
    675      * Finalizes the {@link EventDisplay} after a multi event display.
    676      */
    677     void endMultiEventDisplay() {
    678         if (mLogTable != null) {
    679             mLogTable.setRedraw(true);
    680         }
    681     }
    682 
    683     /**
    684      * Returns the {@link Table} object used to display events, if any.
    685      *
    686      * @return a Table object or <code>null</code>.
    687      */
    688     Table getTable() {
    689         return mLogTable;
    690     }
    691 
    692     /**
    693      * Loads a new {@link EventDisplay} from a storage string. The string must have been created
    694      * with {@link #getStorageString()}.
    695      *
    696      * @param storageString the storage string
    697      * @return a new {@link EventDisplay} or null if the load failed.
    698      */
    699     static EventDisplay load(String storageString) {
    700         if (storageString.length() > 0) {
    701             // the storage string is separated by ':'
    702             String[] values = storageString.split(Pattern.quote(DISPLAY_DATA_STORAGE_SEPARATOR));
    703 
    704             try {
    705                 int index = 0;
    706 
    707                 String name = values[index++];
    708                 int displayType = Integer.parseInt(values[index++]);
    709                 boolean pidFiltering = Boolean.parseBoolean(values[index++]);
    710 
    711                 EventDisplay ed = eventDisplayFactory(displayType, name);
    712                 ed.setPidFiltering(pidFiltering);
    713 
    714                 // because empty sections are removed by String.split(), we have to check
    715                 // the index for those.
    716                 if (index < values.length) {
    717                     ed.loadPidFilters(values[index++]);
    718                 }
    719 
    720                 if (index < values.length) {
    721                     ed.loadValueDescriptors(values[index++]);
    722                 }
    723 
    724                 if (index < values.length) {
    725                     ed.loadOccurrenceDescriptors(values[index++]);
    726                 }
    727 
    728                 ed.updateValueDescriptorCheck();
    729 
    730                 if (index < values.length) {
    731                     ed.mMaximumChartItemAge = Long.parseLong(values[index++]);
    732                 }
    733 
    734                 if (index < values.length) {
    735                     ed.mHistWidth = Long.parseLong(values[index++]);
    736                 }
    737 
    738                 return ed;
    739             } catch (RuntimeException re) {
    740                 // we'll return null below.
    741                 Log.e("ddms", re);
    742             }
    743         }
    744 
    745         return null;
    746     }
    747 
    748     private String getPidStorageString() {
    749         if (mPidFilterList != null) {
    750             StringBuilder sb = new StringBuilder();
    751             boolean first = true;
    752             for (Integer i : mPidFilterList) {
    753                 if (first == false) {
    754                     sb.append(PID_STORAGE_SEPARATOR);
    755                 } else {
    756                     first = false;
    757                 }
    758                 sb.append(i);
    759             }
    760 
    761             return sb.toString();
    762         }
    763         return ""; //$NON-NLS-1$
    764     }
    765 
    766 
    767     private void loadPidFilters(String storageString) {
    768         if (storageString.length() > 0) {
    769             String[] values = storageString.split(Pattern.quote(PID_STORAGE_SEPARATOR));
    770 
    771             for (String value : values) {
    772                 if (mPidFilterList == null) {
    773                     mPidFilterList = new ArrayList<Integer>();
    774                 }
    775                 mPidFilterList.add(Integer.parseInt(value));
    776             }
    777         }
    778     }
    779 
    780     private String getDescriptorStorageString(
    781             ArrayList<? extends OccurrenceDisplayDescriptor> descriptorList) {
    782         StringBuilder sb = new StringBuilder();
    783         boolean first = true;
    784 
    785         for (OccurrenceDisplayDescriptor descriptor : descriptorList) {
    786             if (first == false) {
    787                 sb.append(DESCRIPTOR_STORAGE_SEPARATOR);
    788             } else {
    789                 first = false;
    790             }
    791             sb.append(descriptor.getStorageString());
    792         }
    793 
    794         return sb.toString();
    795     }
    796 
    797     private void loadOccurrenceDescriptors(String storageString) {
    798         if (storageString.length() == 0) {
    799             return;
    800         }
    801 
    802         String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
    803 
    804         for (String value : values) {
    805             OccurrenceDisplayDescriptor desc = new OccurrenceDisplayDescriptor();
    806             desc.loadFrom(value);
    807             mOccurrenceDescriptors.add(desc);
    808         }
    809     }
    810 
    811     private void loadValueDescriptors(String storageString) {
    812         if (storageString.length() == 0) {
    813             return;
    814         }
    815 
    816         String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
    817 
    818         for (String value : values) {
    819             ValueDisplayDescriptor desc = new ValueDisplayDescriptor();
    820             desc.loadFrom(value);
    821             mValueDescriptors.add(desc);
    822         }
    823     }
    824 
    825     /**
    826      * Fills a list with {@link OccurrenceDisplayDescriptor} (or a subclass of it) from another
    827      * list if they are configured to display the {@link EventContainer}
    828      *
    829      * @param event    the event container
    830      * @param fullList the list with all the descriptors.
    831      * @param outList  the list to fill.
    832      */
    833     @SuppressWarnings("unchecked")
    834     private void getDescriptors(EventContainer event,
    835             ArrayList<? extends OccurrenceDisplayDescriptor> fullList,
    836             ArrayList outList) {
    837         for (OccurrenceDisplayDescriptor descriptor : fullList) {
    838             try {
    839                 // first check the event tag.
    840                 if (descriptor.eventTag == event.mTag) {
    841                     // now check if we have a filter on a value
    842                     if (descriptor.filterValueIndex == -1 ||
    843                             event.testValue(descriptor.filterValueIndex, descriptor.filterValue,
    844                                     descriptor.filterCompareMethod)) {
    845                         outList.add(descriptor);
    846                     }
    847                 }
    848             } catch (InvalidTypeException ite) {
    849                 // if the filter for the descriptor was incorrect, we ignore the descriptor.
    850             } catch (ArrayIndexOutOfBoundsException aioobe) {
    851                 // if the index was wrong (the event content may have changed since we setup the
    852                 // display), we do nothing but log the error
    853                 Log.e("Event Log", String.format(
    854                         "ArrayIndexOutOfBoundsException occured when checking %1$d-th value of event %2$d", //$NON-NLS-1$
    855                         descriptor.filterValueIndex, descriptor.eventTag));
    856             }
    857         }
    858     }
    859 
    860     /**
    861      * Filters the {@link com.android.ddmlib.log.EventContainer}, and fills two list of {@link com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor}
    862      * and {@link com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor} configured to display the event.
    863      *
    864      * @param event
    865      * @param valueDescriptors
    866      * @param occurrenceDescriptors
    867      * @return true if the event should be displayed.
    868      */
    869 
    870     protected boolean filterEvent(EventContainer event,
    871             ArrayList<ValueDisplayDescriptor> valueDescriptors,
    872             ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
    873 
    874         // test the pid first (if needed)
    875         if (mPidFiltering && mPidFilterList != null) {
    876             boolean found = false;
    877             for (int pid : mPidFilterList) {
    878                 if (pid == event.pid) {
    879                     found = true;
    880                     break;
    881                 }
    882             }
    883 
    884             if (found == false) {
    885                 return false;
    886             }
    887         }
    888 
    889         // now get the list of matching descriptors
    890         getDescriptors(event, mValueDescriptors, valueDescriptors);
    891         getDescriptors(event, mOccurrenceDescriptors, occurrenceDescriptors);
    892 
    893         // and return whether there is at least one match in either list.
    894         return (valueDescriptors.size() > 0 || occurrenceDescriptors.size() > 0);
    895     }
    896 
    897     /**
    898      * Checks all the {@link ValueDisplayDescriptor} for similarity.
    899      * If all the event values are from the same tag, the method will return EVENT_CHECK_SAME_TAG.
    900      * If all the event/value are the same, the method will return EVENT_CHECK_SAME_VALUE
    901      *
    902      * @return flag as described above
    903      */
    904     private int checkDescriptors() {
    905         if (mValueDescriptors.size() < 2) {
    906             return EVENT_CHECK_SAME_VALUE;
    907         }
    908 
    909         int tag = -1;
    910         int index = -1;
    911         for (ValueDisplayDescriptor display : mValueDescriptors) {
    912             if (tag == -1) {
    913                 tag = display.eventTag;
    914                 index = display.valueIndex;
    915             } else {
    916                 if (tag != display.eventTag) {
    917                     return EVENT_CHECK_FAILED;
    918                 } else {
    919                     if (index != -1) {
    920                         if (index != display.valueIndex) {
    921                             index = -1;
    922                         }
    923                     }
    924                 }
    925             }
    926         }
    927 
    928         if (index == -1) {
    929             return EVENT_CHECK_SAME_TAG;
    930         }
    931 
    932         return EVENT_CHECK_SAME_VALUE;
    933     }
    934 
    935     /**
    936      * Resets the time limit on the chart to be infinite.
    937      */
    938     void resetChartTimeLimit() {
    939         mMaximumChartItemAge = -1;
    940     }
    941 
    942     /**
    943      * Sets the time limit on the charts.
    944      *
    945      * @param timeLimit the time limit in seconds.
    946      */
    947     void setChartTimeLimit(long timeLimit) {
    948         mMaximumChartItemAge = timeLimit;
    949     }
    950 
    951     long getChartTimeLimit() {
    952         return mMaximumChartItemAge;
    953     }
    954 
    955     /**
    956      * m
    957      * Resets the histogram width
    958      */
    959     void resetHistWidth() {
    960         mHistWidth = 1;
    961     }
    962 
    963     /**
    964      * Sets the histogram width
    965      *
    966      * @param histWidth the width in hours
    967      */
    968     void setHistWidth(long histWidth) {
    969         mHistWidth = histWidth;
    970     }
    971 
    972     long getHistWidth() {
    973         return mHistWidth;
    974     }
    975 }
    976