• 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 
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      */
eventDisplayFactory(int type, String name)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      */
newEvent(EventContainer event, EventLogParser logParser)111     abstract void newEvent(EventContainer event, EventLogParser logParser);
112 
113     /**
114      * Resets the display.
115      */
resetUI()116     abstract void resetUI();
117 
118     /**
119      * Gets display type
120      *
121      * @return display type as an integer
122      */
getDisplayType()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      */
createComposite(final Composite parent, EventLogParser logParser, final ILogColumnListener listener)132     abstract Control createComposite(final Composite parent, EventLogParser logParser,
133             final ILogColumnListener listener);
134 
135     interface ILogColumnListener {
columnResized(int index, TableColumn sourceColumn)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 
OccurrenceDisplayDescriptor()151         OccurrenceDisplayDescriptor() {
152         }
153 
OccurrenceDisplayDescriptor(OccurrenceDisplayDescriptor descriptor)154         OccurrenceDisplayDescriptor(OccurrenceDisplayDescriptor descriptor) {
155             replaceWith(descriptor);
156         }
157 
OccurrenceDisplayDescriptor(int eventTag)158         OccurrenceDisplayDescriptor(int eventTag) {
159             this.eventTag = eventTag;
160         }
161 
OccurrenceDisplayDescriptor(int eventTag, int seriesValueIndex)162         OccurrenceDisplayDescriptor(int eventTag, int seriesValueIndex) {
163             this.eventTag = eventTag;
164             this.seriesValueIndex = seriesValueIndex;
165         }
166 
replaceWith(OccurrenceDisplayDescriptor descriptor)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          */
loadFrom(String storageString)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          */
loadFrom(String[] storageStrings, int index)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          */
getStorageString()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 
ValueDisplayDescriptor()250         ValueDisplayDescriptor() {
251             super();
252         }
253 
ValueDisplayDescriptor(ValueDisplayDescriptor descriptor)254         ValueDisplayDescriptor(ValueDisplayDescriptor descriptor) {
255             super();
256             replaceWith(descriptor);
257         }
258 
ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex)259         ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex) {
260             super(eventTag);
261             this.valueName = valueName;
262             this.valueIndex = valueIndex;
263         }
264 
ValueDisplayDescriptor(int eventTag, String valueName, int valueIndex, int seriesValueIndex)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
replaceWith(OccurrenceDisplayDescriptor descriptor)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
loadFrom(String[] storageStrings, int index)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
getStorageString()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 
EventDisplay(String name)365     EventDisplay(String name) {
366         mName = name;
367     }
368 
clone(EventDisplay from)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      */
getStorageString()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 
setName(String name)418     void setName(String name) {
419         mName = name;
420     }
421 
getName()422     String getName() {
423         return mName;
424     }
425 
setPidFiltering(boolean filterByPid)426     void setPidFiltering(boolean filterByPid) {
427         mPidFiltering = filterByPid;
428     }
429 
getPidFiltering()430     boolean getPidFiltering() {
431         return mPidFiltering;
432     }
433 
setPidFilterList(ArrayList<Integer> pids)434     void setPidFilterList(ArrayList<Integer> pids) {
435         if (mPidFiltering == false) {
436             throw new InvalidParameterException();
437         }
438 
439         mPidFilterList = pids;
440     }
441 
getPidFilterList()442     ArrayList<Integer> getPidFilterList() {
443         return mPidFilterList;
444     }
445 
addPidFiler(int pid)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      */
getValueDescriptors()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      */
updateValueDescriptorCheck()469     void updateValueDescriptorCheck() {
470         mValueDescriptorCheck = checkDescriptors();
471     }
472 
473     /**
474      * Returns an iterator to the list of {@link OccurrenceDisplayDescriptor}.
475      */
getOccurrenceDescriptors()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      */
addDescriptor(OccurrenceDisplayDescriptor descriptor)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      */
getDescriptor( Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index)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      */
removeDescriptor(Class<? extends OccurrenceDisplayDescriptor> descriptorClass, int index)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 
createCompositeChart(final Composite parent, EventLogParser logParser, String title)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             public void chartChanged(ChartChangeEvent event) {
564                 ChartChangeEventType type = event.getType();
565                 if (type == ChartChangeEventType.GENERAL) {
566                     // because the value we need (rangeCrosshair and domainCrosshair) are
567                     // updated on the draw, but the notification happens before the draw,
568                     // we process the click in a future runnable!
569                     parent.getDisplay().asyncExec(new Runnable() {
570                         public void run() {
571                             processClick(xyPlot);
572                         }
573                     });
574                 }
575             }
576         });
577 
578         mChartComposite = new ChartComposite(parent, SWT.BORDER, mChart,
579                 ChartComposite.DEFAULT_WIDTH,
580                 ChartComposite.DEFAULT_HEIGHT,
581                 ChartComposite.DEFAULT_MINIMUM_DRAW_WIDTH,
582                 ChartComposite.DEFAULT_MINIMUM_DRAW_HEIGHT,
583                 3000, // max draw width. We don't want it to zoom, so we put a big number
584                 3000, // max draw height. We don't want it to zoom, so we put a big number
585                 true,  // off-screen buffer
586                 true,  // properties
587                 true,  // save
588                 true,  // print
589                 true,  // zoom
590                 true);   // tooltips
591 
592         mChartComposite.addDisposeListener(new DisposeListener() {
593             public void widgetDisposed(DisposeEvent e) {
594                 mValueTypeDataSetMap.clear();
595                 mDataSetCount = 0;
596                 mOccurrenceDataSet = null;
597                 mChart = null;
598                 mChartComposite = null;
599                 mValueDescriptorSeriesMap.clear();
600                 mOcurrenceDescriptorSeriesMap.clear();
601             }
602         });
603 
604         return mChartComposite;
605 
606     }
607 
processClick(XYPlot xyPlot)608     private void processClick(XYPlot xyPlot) {
609         double rangeValue = xyPlot.getRangeCrosshairValue();
610         if (rangeValue != 0) {
611             double domainValue = xyPlot.getDomainCrosshairValue();
612 
613             Millisecond msec = new Millisecond(new Date((long) domainValue));
614 
615             // look for values in the dataset that contains data at this TimePeriod
616             Set<ValueDisplayDescriptor> descKeys = mValueDescriptorSeriesMap.keySet();
617 
618             for (ValueDisplayDescriptor descKey : descKeys) {
619                 HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descKey);
620 
621                 Set<Integer> pidKeys = map.keySet();
622 
623                 for (Integer pidKey : pidKeys) {
624                     TimeSeries series = map.get(pidKey);
625 
626                     Number value = series.getValue(msec);
627                     if (value != null) {
628                         // found a match. lets check against the actual value.
629                         if (value.doubleValue() == rangeValue) {
630 
631                             return;
632                         }
633                     }
634                 }
635             }
636         }
637     }
638 
639 
640     /**
641      * Resizes the <code>index</code>-th column of the log {@link Table} (if applicable).
642      * Subclasses can override if necessary.
643      * <p/>
644      * This does nothing if the <code>Table</code> object is <code>null</code> (because the display
645      * type does not use a column) or if the <code>index</code>-th column is in fact the originating
646      * column passed as argument.
647      *
648      * @param index        the index of the column to resize
649      * @param sourceColumn the original column that was resize, and on which we need to sync the
650      *                     index-th column width.
651      */
resizeColumn(int index, TableColumn sourceColumn)652     void resizeColumn(int index, TableColumn sourceColumn) {
653     }
654 
655     /**
656      * Sets the current {@link EventLogParser} object.
657      * Subclasses can override if necessary.
658      */
setNewLogParser(EventLogParser logParser)659     protected void setNewLogParser(EventLogParser logParser) {
660     }
661 
662     /**
663      * Prepares the {@link EventDisplay} for a multi event display.
664      */
startMultiEventDisplay()665     void startMultiEventDisplay() {
666         if (mLogTable != null) {
667             mLogTable.setRedraw(false);
668         }
669     }
670 
671     /**
672      * Finalizes the {@link EventDisplay} after a multi event display.
673      */
endMultiEventDisplay()674     void endMultiEventDisplay() {
675         if (mLogTable != null) {
676             mLogTable.setRedraw(true);
677         }
678     }
679 
680     /**
681      * Returns the {@link Table} object used to display events, if any.
682      *
683      * @return a Table object or <code>null</code>.
684      */
getTable()685     Table getTable() {
686         return mLogTable;
687     }
688 
689     /**
690      * Loads a new {@link EventDisplay} from a storage string. The string must have been created
691      * with {@link #getStorageString()}.
692      *
693      * @param storageString the storage string
694      * @return a new {@link EventDisplay} or null if the load failed.
695      */
load(String storageString)696     static EventDisplay load(String storageString) {
697         if (storageString.length() > 0) {
698             // the storage string is separated by ':'
699             String[] values = storageString.split(Pattern.quote(DISPLAY_DATA_STORAGE_SEPARATOR));
700 
701             try {
702                 int index = 0;
703 
704                 String name = values[index++];
705                 int displayType = Integer.parseInt(values[index++]);
706                 boolean pidFiltering = Boolean.parseBoolean(values[index++]);
707 
708                 EventDisplay ed = eventDisplayFactory(displayType, name);
709                 ed.setPidFiltering(pidFiltering);
710 
711                 // because empty sections are removed by String.split(), we have to check
712                 // the index for those.
713                 if (index < values.length) {
714                     ed.loadPidFilters(values[index++]);
715                 }
716 
717                 if (index < values.length) {
718                     ed.loadValueDescriptors(values[index++]);
719                 }
720 
721                 if (index < values.length) {
722                     ed.loadOccurrenceDescriptors(values[index++]);
723                 }
724 
725                 ed.updateValueDescriptorCheck();
726 
727                 if (index < values.length) {
728                     ed.mMaximumChartItemAge = Long.parseLong(values[index++]);
729                 }
730 
731                 if (index < values.length) {
732                     ed.mHistWidth = Long.parseLong(values[index++]);
733                 }
734 
735                 return ed;
736             } catch (RuntimeException re) {
737                 // we'll return null below.
738                 Log.e("ddms", re);
739             }
740         }
741 
742         return null;
743     }
744 
getPidStorageString()745     private String getPidStorageString() {
746         if (mPidFilterList != null) {
747             StringBuilder sb = new StringBuilder();
748             boolean first = true;
749             for (Integer i : mPidFilterList) {
750                 if (first == false) {
751                     sb.append(PID_STORAGE_SEPARATOR);
752                 } else {
753                     first = false;
754                 }
755                 sb.append(i);
756             }
757 
758             return sb.toString();
759         }
760         return ""; //$NON-NLS-1$
761     }
762 
763 
loadPidFilters(String storageString)764     private void loadPidFilters(String storageString) {
765         if (storageString.length() > 0) {
766             String[] values = storageString.split(Pattern.quote(PID_STORAGE_SEPARATOR));
767 
768             for (String value : values) {
769                 if (mPidFilterList == null) {
770                     mPidFilterList = new ArrayList<Integer>();
771                 }
772                 mPidFilterList.add(Integer.parseInt(value));
773             }
774         }
775     }
776 
getDescriptorStorageString( ArrayList<? extends OccurrenceDisplayDescriptor> descriptorList)777     private String getDescriptorStorageString(
778             ArrayList<? extends OccurrenceDisplayDescriptor> descriptorList) {
779         StringBuilder sb = new StringBuilder();
780         boolean first = true;
781 
782         for (OccurrenceDisplayDescriptor descriptor : descriptorList) {
783             if (first == false) {
784                 sb.append(DESCRIPTOR_STORAGE_SEPARATOR);
785             } else {
786                 first = false;
787             }
788             sb.append(descriptor.getStorageString());
789         }
790 
791         return sb.toString();
792     }
793 
loadOccurrenceDescriptors(String storageString)794     private void loadOccurrenceDescriptors(String storageString) {
795         if (storageString.length() == 0) {
796             return;
797         }
798 
799         String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
800 
801         for (String value : values) {
802             OccurrenceDisplayDescriptor desc = new OccurrenceDisplayDescriptor();
803             desc.loadFrom(value);
804             mOccurrenceDescriptors.add(desc);
805         }
806     }
807 
loadValueDescriptors(String storageString)808     private void loadValueDescriptors(String storageString) {
809         if (storageString.length() == 0) {
810             return;
811         }
812 
813         String[] values = storageString.split(Pattern.quote(DESCRIPTOR_STORAGE_SEPARATOR));
814 
815         for (String value : values) {
816             ValueDisplayDescriptor desc = new ValueDisplayDescriptor();
817             desc.loadFrom(value);
818             mValueDescriptors.add(desc);
819         }
820     }
821 
822     /**
823      * Fills a list with {@link OccurrenceDisplayDescriptor} (or a subclass of it) from another
824      * list if they are configured to display the {@link EventContainer}
825      *
826      * @param event    the event container
827      * @param fullList the list with all the descriptors.
828      * @param outList  the list to fill.
829      */
830     @SuppressWarnings("unchecked")
getDescriptors(EventContainer event, ArrayList<? extends OccurrenceDisplayDescriptor> fullList, ArrayList outList)831     private void getDescriptors(EventContainer event,
832             ArrayList<? extends OccurrenceDisplayDescriptor> fullList,
833             ArrayList outList) {
834         for (OccurrenceDisplayDescriptor descriptor : fullList) {
835             try {
836                 // first check the event tag.
837                 if (descriptor.eventTag == event.mTag) {
838                     // now check if we have a filter on a value
839                     if (descriptor.filterValueIndex == -1 ||
840                             event.testValue(descriptor.filterValueIndex, descriptor.filterValue,
841                                     descriptor.filterCompareMethod)) {
842                         outList.add(descriptor);
843                     }
844                 }
845             } catch (InvalidTypeException ite) {
846                 // if the filter for the descriptor was incorrect, we ignore the descriptor.
847             } catch (ArrayIndexOutOfBoundsException aioobe) {
848                 // if the index was wrong (the event content may have changed since we setup the
849                 // display), we do nothing but log the error
850                 Log.e("Event Log", String.format(
851                         "ArrayIndexOutOfBoundsException occured when checking %1$d-th value of event %2$d", //$NON-NLS-1$
852                         descriptor.filterValueIndex, descriptor.eventTag));
853             }
854         }
855     }
856 
857     /**
858      * Filters the {@link com.android.ddmlib.log.EventContainer}, and fills two list of {@link com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor}
859      * and {@link com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor} configured to display the event.
860      *
861      * @param event
862      * @param valueDescriptors
863      * @param occurrenceDescriptors
864      * @return true if the event should be displayed.
865      */
866 
filterEvent(EventContainer event, ArrayList<ValueDisplayDescriptor> valueDescriptors, ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors)867     protected boolean filterEvent(EventContainer event,
868             ArrayList<ValueDisplayDescriptor> valueDescriptors,
869             ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
870 
871         // test the pid first (if needed)
872         if (mPidFiltering && mPidFilterList != null) {
873             boolean found = false;
874             for (int pid : mPidFilterList) {
875                 if (pid == event.pid) {
876                     found = true;
877                     break;
878                 }
879             }
880 
881             if (found == false) {
882                 return false;
883             }
884         }
885 
886         // now get the list of matching descriptors
887         getDescriptors(event, mValueDescriptors, valueDescriptors);
888         getDescriptors(event, mOccurrenceDescriptors, occurrenceDescriptors);
889 
890         // and return whether there is at least one match in either list.
891         return (valueDescriptors.size() > 0 || occurrenceDescriptors.size() > 0);
892     }
893 
894     /**
895      * Checks all the {@link ValueDisplayDescriptor} for similarity.
896      * If all the event values are from the same tag, the method will return EVENT_CHECK_SAME_TAG.
897      * If all the event/value are the same, the method will return EVENT_CHECK_SAME_VALUE
898      *
899      * @return flag as described above
900      */
checkDescriptors()901     private int checkDescriptors() {
902         if (mValueDescriptors.size() < 2) {
903             return EVENT_CHECK_SAME_VALUE;
904         }
905 
906         int tag = -1;
907         int index = -1;
908         for (ValueDisplayDescriptor display : mValueDescriptors) {
909             if (tag == -1) {
910                 tag = display.eventTag;
911                 index = display.valueIndex;
912             } else {
913                 if (tag != display.eventTag) {
914                     return EVENT_CHECK_FAILED;
915                 } else {
916                     if (index != -1) {
917                         if (index != display.valueIndex) {
918                             index = -1;
919                         }
920                     }
921                 }
922             }
923         }
924 
925         if (index == -1) {
926             return EVENT_CHECK_SAME_TAG;
927         }
928 
929         return EVENT_CHECK_SAME_VALUE;
930     }
931 
932     /**
933      * Resets the time limit on the chart to be infinite.
934      */
resetChartTimeLimit()935     void resetChartTimeLimit() {
936         mMaximumChartItemAge = -1;
937     }
938 
939     /**
940      * Sets the time limit on the charts.
941      *
942      * @param timeLimit the time limit in seconds.
943      */
setChartTimeLimit(long timeLimit)944     void setChartTimeLimit(long timeLimit) {
945         mMaximumChartItemAge = timeLimit;
946     }
947 
getChartTimeLimit()948     long getChartTimeLimit() {
949         return mMaximumChartItemAge;
950     }
951 
952     /**
953      * m
954      * Resets the histogram width
955      */
resetHistWidth()956     void resetHistWidth() {
957         mHistWidth = 1;
958     }
959 
960     /**
961      * Sets the histogram width
962      *
963      * @param histWidth the width in hours
964      */
setHistWidth(long histWidth)965     void setHistWidth(long histWidth) {
966         mHistWidth = histWidth;
967     }
968 
getHistWidth()969     long getHistWidth() {
970         return mHistWidth;
971     }
972 }
973