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