/******************************************************************************* * Copyright (c) 2014 École Polytechnique de Montréal * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Florian Wininger - Initial API and implementation * Geneviève Bastien - Review of the initial implementation *******************************************************************************/ package org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.views.timegraph; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.module.XmlUtils; import org.eclipse.tracecompass.internal.tmf.analysis.xml.core.stateprovider.TmfXmlStrings; import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.TmfXmlUiStrings; import org.eclipse.tracecompass.internal.tmf.analysis.xml.ui.views.timegraph.XmlEntry.EntryDisplayType; import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem; import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider; import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent; import org.w3c.dom.Element; /** * Presentation provider for the XML view, based on the generic TMF presentation * provider. * * TODO: This should support colors/states defined for each entry element in the * XML element. * * @author Florian Wininger */ public class XmlPresentationProvider extends TimeGraphPresentationProvider { private static final long[] COLOR_SEED = { 0x0000ff, 0xff0000, 0x00ff00, 0xff00ff, 0x00ffff, 0xffff00, 0x000000, 0xf07300 }; private static final int COLOR_MASK = 0xffffff; private List<StateItem> stateValues = new ArrayList<>(); /* * Maps the value of an event with the corresponding index in the * stateValues list */ private Map<Integer, Integer> stateIndex = new HashMap<>(); @Override public int getStateTableIndex(ITimeEvent event) { if (event instanceof TimeEvent && ((TimeEvent) event).hasValue()) { TimeEvent tcEvent = (TimeEvent) event; XmlEntry entry = (XmlEntry) event.getEntry(); int value = tcEvent.getValue(); if (entry.getType() == EntryDisplayType.DISPLAY) { // Draw state only if state is already known Integer index = stateIndex.get(value); if (index != null) { return index; } } } return INVISIBLE; } @Override public StateItem[] getStateTable() { return stateValues.toArray(new StateItem[stateValues.size()]); } @Override public String getEventName(ITimeEvent event) { if (event instanceof TimeEvent && ((TimeEvent) event).hasValue()) { TimeEvent tcEvent = (TimeEvent) event; XmlEntry entry = (XmlEntry) event.getEntry(); int value = tcEvent.getValue(); if (entry.getType() == EntryDisplayType.DISPLAY) { Integer index = stateIndex.get(value); if (index != null) { String rgb = stateValues.get(index.intValue()).getStateString(); return rgb; } } return null; } return Messages.XmlPresentationProvider_MultipleStates; } @Override public Map<String, String> getEventHoverToolTipInfo(ITimeEvent event, long hoverTime) { /* * TODO: Add the XML elements to support adding extra information in the * tooltips and implement this */ return Collections.EMPTY_MAP; } @Override public void postDrawEvent(ITimeEvent event, Rectangle bounds, GC gc) { /* * TODO Add the XML elements to support texts in intervals and implement * this */ } @Override public void postDrawEntry(ITimeGraphEntry entry, Rectangle bounds, GC gc) { } /** * Loads the states from a {@link TmfXmlUiStrings#TIME_GRAPH_VIEW} XML * element * * @param viewElement * The XML view element */ public synchronized void loadNewStates(@NonNull Element viewElement) { stateValues.clear(); stateIndex.clear(); List<Element> states = XmlUtils.getChildElements(viewElement, TmfXmlStrings.DEFINED_VALUE); for (Element state : states) { int value = Integer.parseInt(state.getAttribute(TmfXmlStrings.VALUE)); String name = state.getAttribute(TmfXmlStrings.NAME); String color = state.getAttribute(TmfXmlStrings.COLOR); addOrUpdateState(value, name, color); } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { fireColorSettingsChanged(); } }); } /** * Add a new state in the time graph view. This allow to define at runtime * new states that cannot be known at the conception of this analysis. * * @param name * The string associated with the state * @return the value for this state */ public synchronized int addState(String name) { // Find a value for this name, start at 10000 int value = 10000; while (stateIndex.get(value) != null) { value++; } addOrUpdateState(value, name, ""); //$NON-NLS-1$ Display.getDefault().asyncExec(new Runnable() { @Override public void run() { fireColorSettingsChanged(); } }); return value; } private synchronized void addOrUpdateState(int value, String name, String color) { // FIXME Allow this case if (value < 0) { return; } final RGB colorRGB = (color.startsWith(TmfXmlStrings.COLOR_PREFIX)) ? parseColor(color) : calcColor(name); StateItem item = new StateItem(colorRGB, name); Integer index = stateIndex.get(value); if (index == null) { /* Add the new state value */ stateIndex.put(value, stateValues.size()); stateValues.add(item); } else { /* Override a previous state value */ stateValues.set(index, item); } } private static RGB parseColor(String color) { RGB colorRGB; Integer hex = Integer.parseInt(color.substring(1), 16); int hex1 = hex.intValue() % 256; int hex2 = (hex.intValue() / 256) % 256; int hex3 = (hex.intValue() / (256 * 256)) % 256; colorRGB = new RGB(hex3, hex2, hex1); return colorRGB; } /* * This method will always return the same color for a same name, no matter * the value, so that different traces with the same XML analysis will * display identically states with the same name. */ private static RGB calcColor(String name) { long hash = name.hashCode(); // hashcodes can be Integer.MIN_VALUE. long base = COLOR_SEED[(int) (Math.abs(hash) % COLOR_SEED.length)]; int x = (int) ((hash & COLOR_MASK) ^ base); final int r = (x >> 16) & 0xff; final int g = (x >> 8) & 0xff; final int b = x & 0xff; return new RGB(r, g, b); } }