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