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