• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
eventDisplayFactory(int type, String name)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      */
newEvent(EventContainer event, EventLogParser logParser)110     abstract void newEvent(EventContainer event, EventLogParser logParser);
111 
112     /**
113      * Resets the display.
114      */
resetUI()115     abstract void resetUI();
116 
117     /**
118      * Gets display type
119      *
120      * @return display type as an integer
121      */
getDisplayType()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      */
createComposite(final Composite parent, EventLogParser logParser, final ILogColumnListener listener)131     abstract Control createComposite(final Composite parent, EventLogParser logParser,
132             final ILogColumnListener listener);
133 
134     interface ILogColumnListener {
columnResized(int index, TableColumn sourceColumn)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 
OccurrenceDisplayDescriptor()150         OccurrenceDisplayDescriptor() {
151         }
152 
OccurrenceDisplayDescriptor(OccurrenceDisplayDescriptor descriptor)153         OccurrenceDisplayDescriptor(OccurrenceDisplayDescriptor descriptor) {
154             replaceWith(descriptor);
155         }
156 
OccurrenceDisplayDescriptor(int eventTag)157         OccurrenceDisplayDescriptor(int eventTag) {
158             this.eventTag = eventTag;
159         }
160 
OccurrenceDisplayDescriptor(int eventTag, int seriesValueIndex)161         OccurrenceDisplayDescriptor(int eventTag, int seriesValueIndex) {
162             this.eventTag = eventTag;
163             this.seriesValueIndex = seriesValueIndex;
164         }
165 
replaceWith(OccurrenceDisplayDescriptor descriptor)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          */
loadFrom(String storageString)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          */
loadFrom(String[] storageStrings, int index)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          */
getStorageString()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 
ValueDisplayDescriptor()249         ValueDisplayDescriptor() {
250             super();
251         }
252 
ValueDisplayDescriptor(ValueDisplayDescriptor descriptor)253         ValueDisplayDescriptor(ValueDisplayDescriptor descriptor) {
254             super();
255             replaceWith(descriptor);
256         }
257 
ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex)258         ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex) {
259             super(eventTag);
260             this.valueName = valueName;
261             this.valueIndex = valueIndex;
262         }
263 
ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex, int seriesValueIndex)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
replaceWith(OccurrenceDisplayDescriptor descriptor)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
loadFrom(String[] storageStrings, int index)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
getStorageString()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 
EventDisplay(String name)364     EventDisplay(String name) {
365         mName = name;
366     }
367 
clone(EventDisplay from)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      */
getStorageString()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 
setName(String name)417     void setName(String name) {
418         mName = name;
419     }
420 
getName()421     String getName() {
422         return mName;
423     }
424 
setPidFiltering(boolean filterByPid)425     void setPidFiltering(boolean filterByPid) {
426         mPidFiltering = filterByPid;
427     }
428 
getPidFiltering()429     boolean getPidFiltering() {
430         return mPidFiltering;
431     }
432 
setPidFilterList(ArrayList<Integer> pids)433     void setPidFilterList(ArrayList<Integer> pids) {
434         if (mPidFiltering == false) {
435             new InvalidParameterException();
436         }
437 
438         mPidFilterList = pids;
439     }
440 
getPidFilterList()441     ArrayList<Integer> getPidFilterList() {
442         return mPidFilterList;
443     }
444 
addPidFiler(int pid)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      */
getValueDescriptors()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      */
updateValueDescriptorCheck()468     void updateValueDescriptorCheck() {
469         mValueDescriptorCheck = checkDescriptors();
470     }
471 
472     /**
473      * Returns an iterator to the list of {@link OccurrenceDisplayDescriptor}.
474      */
getOccurrenceDescriptors()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      */
addDescriptor(OccurrenceDisplayDescriptor descriptor)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      */
getDescriptor( Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index)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      */
removeDescriptor(Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index)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 
createCompositeChart(final Composite parent, EventLogParser logParser, String title)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 
processClick(XYPlot xyPlot)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      */
resizeColumn(int index, TableColumn sourceColumn)651     void resizeColumn(int index, TableColumn sourceColumn) {
652     }
653 
654     /**
655      * Sets the current {@link EventLogParser} object.
656      * Subclasses can override if necessary.
657      */
setNewLogParser(EventLogParser logParser)658     protected void setNewLogParser(EventLogParser logParser) {
659     }
660 
661     /**
662      * Prepares the {@link EventDisplay} for a multi event display.
663      */
startMultiEventDisplay()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      */
endMultiEventDisplay()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      */
getTable()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      */
load(String storageString)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 
getPidStorageString()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 
loadPidFilters(String storageString)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 
getDescriptorStorageString( ArrayList<? extends OccurrenceDisplayDescriptor> descriptorList)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 
loadOccurrenceDescriptors(String storageString)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 
loadValueDescriptors(String storageString)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")
getDescriptors(EventContainer event, ArrayList<? extends OccurrenceDisplayDescriptor> fullList, ArrayList outList)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 
filterEvent(EventContainer event, ArrayList<ValueDisplayDescriptor> valueDescriptors, ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors)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      */
checkDescriptors()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      */
resetChartTimeLimit()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      */
setChartTimeLimit(long timeLimit)943     void setChartTimeLimit(long timeLimit) {
944         mMaximumChartItemAge = timeLimit;
945     }
946 
getChartTimeLimit()947     long getChartTimeLimit() {
948         return mMaximumChartItemAge;
949     }
950 
951     /**
952      * m
953      * Resets the histogram width
954      */
resetHistWidth()955     void resetHistWidth() {
956         mHistWidth = 1;
957     }
958 
959     /**
960      * Sets the histogram width
961      *
962      * @param histWidth the width in hours
963      */
setHistWidth(long histWidth)964     void setHistWidth(long histWidth) {
965         mHistWidth = histWidth;
966     }
967 
getHistWidth()968     long getHistWidth() {
969         return mHistWidth;
970     }
971 }
972