/******************************************************************************* * Copyright (c) 2012, 2016 Ericsson, É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: * Patrick Tasse - Initial API and implementation * Geneviève Bastien - Move code to provide base classes for time graph view *******************************************************************************/ package org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.swt.SWT; /** * An entry for use in the time graph views */ public class TimeGraphEntry implements ITimeGraphEntry { /** Entry's parent */ private TimeGraphEntry fParent = null; /** List of child entries */ private final List<@NonNull TimeGraphEntry> fChildren = new CopyOnWriteArrayList<>(); /** Name of this entry (text to show) */ private String fName; private long fStartTime = SWT.DEFAULT; private long fEndTime = SWT.DEFAULT; private @NonNull List<ITimeEvent> fEventList = new ArrayList<>(); private @NonNull List<ITimeEvent> fZoomedEventList = new ArrayList<>(); private Comparator<ITimeGraphEntry> fComparator; /** * Constructor * * @param name * The name of this entry * @param startTime * The start time of this entry * @param endTime * The end time of this entry */ public TimeGraphEntry(String name, long startTime, long endTime) { fName = name; fStartTime = startTime; fEndTime = endTime; } // --------------------------------------------- // Getters and setters // --------------------------------------------- /** * @since 2.0 */ @Override public TimeGraphEntry getParent() { return fParent; } /** * Sets the entry's parent * * @param entry The new parent entry * @since 2.0 */ public void setParent(TimeGraphEntry entry) { fParent = entry; } @Override public synchronized boolean hasChildren() { return fChildren.size() > 0; } @Override public synchronized List<@NonNull TimeGraphEntry> getChildren() { return fChildren; } /** * Clear the children of the entry * * @since 2.0 */ public synchronized void clearChildren() { fChildren.clear(); } @Override public String getName() { return fName; } /** * Update the entry name * * @param name * the updated entry name */ public void setName(String name) { fName = name; } @Override public long getStartTime() { return fStartTime; } @Override public long getEndTime() { return fEndTime; } /** * Updates the end time * * @param endTime * the end time */ public void updateEndTime(long endTime) { fEndTime = Math.max(endTime, fEndTime); } @Override public boolean hasTimeEvents() { return true; } @Override public Iterator<@NonNull ITimeEvent> getTimeEventsIterator() { if (hasTimeEvents()) { return new EventIterator(fEventList, fZoomedEventList); } return null; } @Override public Iterator<@NonNull ITimeEvent> getTimeEventsIterator(long startTime, long stopTime, long visibleDuration) { if (!hasTimeEvents()) { return null; } return new EventIterator(fEventList, fZoomedEventList, startTime, stopTime); } /** * Add an event to this entry's event list. If necessary, update the start * and end time of the entry. If the event list's last event starts at the * same time as the event to add, it is replaced by the new event. * * @param event * The time event to add */ public void addEvent(ITimeEvent event) { long start = event.getTime(); long end = start + event.getDuration(); int lastIndex = fEventList.size() - 1; if (lastIndex >= 0 && fEventList.get(lastIndex).getTime() == event.getTime()) { fEventList.set(lastIndex, event); } else { fEventList.add(event); } if (event instanceof NullTimeEvent) { /* A NullTimeEvent should not affect the entry bounds */ return; } if (fStartTime == SWT.DEFAULT || start < fStartTime) { fStartTime = start; } if (fEndTime == SWT.DEFAULT || end > fEndTime) { fEndTime = end; } } /** * Set the general event list of this entry. The list should be modifiable * but will only increase in size over time. * * @param eventList * The modifiable list of time events, or null to clear the list */ public void setEventList(List<ITimeEvent> eventList) { if (eventList != null) { fEventList = eventList; } else { fEventList = new ArrayList<>(); } } /** * Set the zoomed event list of this entry. The list should be modifiable * but will only increase in size over time. * * @param eventList * The modifiable list of time events, or null to clear the list */ public void setZoomedEventList(List<ITimeEvent> eventList) { if (eventList != null) { fZoomedEventList = eventList; } else { fZoomedEventList = new ArrayList<>(); } } /** * Add an event to this entry's zoomed event list. If necessary, update the * start and end time of the entry. If the zoomed event list's last event * starts at the same time as the event to add, it is replaced by the new * event. If the new event starts before the zoomed event list's last event, * the new event is ignored and is assumed to be already part of the list. * If the new event starts before the zoomed event list's first event, the * list is assumed to be incomplete and is cleared, and the event is added. * * @param event * The time event to add * @since 1.1 */ public void addZoomedEvent(ITimeEvent event) { long start = event.getTime(); long end = start + event.getDuration(); int lastIndex = fZoomedEventList.size() - 1; long lastStart = lastIndex >= 0 ? fZoomedEventList.get(lastIndex).getTime() : Long.MIN_VALUE; if (start > lastStart) { fZoomedEventList.add(event); } else if (start == lastStart) { fZoomedEventList.set(lastIndex, event); } else if (start < fZoomedEventList.get(0).getTime()) { fZoomedEventList.clear(); fZoomedEventList.add(event); } if (event instanceof NullTimeEvent) { /* A NullTimeEvent should not affect the entry bounds */ return; } if (fStartTime == SWT.DEFAULT || start < fStartTime) { fStartTime = start; } if (fEndTime == SWT.DEFAULT || end > fEndTime) { fEndTime = end; } } /** * Add a child entry to this one. If a comparator was previously set with * {@link #sortChildren(Comparator)}, the entry will be inserted in its * sort-order position. Otherwise it will be added to the end of the list. * * @param child * The child entry */ public synchronized void addChild(@NonNull TimeGraphEntry child) { if (fComparator == null) { addChild(fChildren.size(), child); } else { int i = Collections.binarySearch(fChildren, child, fComparator); /* Deal with negative insertion points from binarySearch */ i = i >= 0 ? i : -i - 1; addChild(i, child); } } /** * Add a child entry to this one at the specified position * * @param index * Index at which the specified entry is to be inserted * @param child * The child entry * @since 2.0 */ public synchronized void addChild(int index, @NonNull TimeGraphEntry child) { if (child.getParent() == this) { return; } if (child.getParent() != null) { child.getParent().removeChild(child); } child.setParent(this); fChildren.add(index, child); } /** * Remove a child entry from this one. * * @param child * The child entry * @since 2.0 */ public synchronized void removeChild(@NonNull TimeGraphEntry child) { if (child.getParent() == this) { child.setParent(null); } fChildren.remove(child); } /** * Sort the children of this entry using the provided comparator. Subsequent * calls to {@link #addChild(TimeGraphEntry)} will use this comparator to * maintain the sort order. * * @param comparator * The entry comparator */ public synchronized void sortChildren(Comparator<ITimeGraphEntry> comparator) { fComparator = comparator; if (comparator == null) { return; } @NonNull TimeGraphEntry[] array = fChildren.toArray(new @NonNull TimeGraphEntry[0]); Arrays.sort(array, comparator); fChildren.clear(); fChildren.addAll(Arrays.asList(array)); } @Override public String toString() { return getClass().getSimpleName() + '(' + fName + ')'; } /** * @since 2.0 */ @Override public boolean matches(@NonNull Pattern pattern) { // Default implementation return pattern.matcher(fName).find(); } }