Mercurial > hg > release > thermostat-0.5
changeset 880:acbfd9752b11
New thread timeline
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-December/004934.html
reviewed-by: rkennek
line wrap: on
line diff
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/GraphicsUtils.java Thu Dec 20 14:49:16 2012 +0100 +++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/GraphicsUtils.java Thu Dec 20 19:12:54 2012 +0100 @@ -92,4 +92,8 @@ Paint paint = new GradientPaint(x, 0, start, 0, height, stop); g.setPaint(paint); } + + public Color deriveWithAlpha(Color color, int alpha) { + return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); + } }
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ActionButtonTest.java Thu Dec 20 14:49:16 2012 +0100 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/components/ActionButtonTest.java Thu Dec 20 19:12:54 2012 +0100 @@ -39,6 +39,9 @@ import java.awt.Color; import java.awt.Component; import java.awt.Graphics; +import java.util.Random; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; import javax.swing.Icon; import javax.swing.JFrame; @@ -51,6 +54,7 @@ import org.fest.swing.fixture.FrameFixture; import org.fest.swing.fixture.JButtonFixture; import org.junit.After; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,17 +63,26 @@ public class ActionButtonTest { private FrameFixture frameFixture; + private Preferences prefs; @BeforeClass public static void setUpOnce() { FailOnThreadViolationRepaintManager.install(); } + + @Before + public void setUp() { + Random random = new Random(); + prefs = Preferences.userRoot().node(HeaderPanelTest.class.getName() + "." + random.nextInt()); + } + @After - public void tearDown() { + public void tearDown() throws BackingStoreException { if (frameFixture != null) { frameFixture.cleanUp(); } + prefs.removeNode(); } @Test @@ -81,7 +94,7 @@ JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - HeaderPanel header = new HeaderPanel(); + HeaderPanel header = new HeaderPanel(prefs, "wrong"); header.setHeader("Test"); Icon icon = new Icon() {
--- a/thread/client-common/pom.xml Thu Dec 20 14:49:16 2012 +0100 +++ b/thread/client-common/pom.xml Thu Dec 20 19:12:54 2012 +0100 @@ -111,6 +111,7 @@ <Export-Package> com.redhat.thermostat.thread.client.common, com.redhat.thermostat.thread.client.common.view, + com.redhat.thermostat.thread.client.common.model.timeline, com.redhat.thermostat.thread.client.common.locale, com.redhat.thermostat.thread.client.common.chart, com.redhat.thermostat.thread.client.common.collector,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadState.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,47 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.common; + +public enum ThreadState { + NEW, + RUNNABLE, + BLOCKED, + WAITING, + TIMED_WAITING, + TERMINATED, + UNKNOWN, +}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/ThreadTimelineBean.java Thu Dec 20 14:49:16 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,160 +0,0 @@ -/* - * Copyright 2012 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.thread.client.common; - -import java.util.Date; - -public class ThreadTimelineBean implements Cloneable { - - private long startTime; - private long stopTime; - private String name; - private Thread.State state; - - private boolean highlight; - - public boolean isHighlight() { - return highlight; - } - - public void setHighlight(boolean highlight) { - this.highlight = highlight; - } - - public Thread.State getState() { - return state; - } - - public void setState(Thread.State state) { - this.state = state; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public long getStartTime() { - return startTime; - } - - public void setStartTime(long startTime) { - this.startTime = startTime; - } - - public long getStopTime() { - return stopTime; - } - - public void setStopTime(long stopTime) { - this.stopTime = stopTime; - } - - @Override - public String toString() { - return "ThreadTimelineBean [name=" + name + ", state=" + state - + ", startTime=" + startTime + " (" + new Date(startTime) + ")" - + ", stopTime=" + stopTime + " (" + new Date(stopTime) + ")]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + (int) (startTime ^ (startTime >>> 32)); - result = prime * result + ((state == null) ? 0 : state.hashCode()); - result = prime * result + (int) (stopTime ^ (stopTime >>> 32)); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ThreadTimelineBean other = (ThreadTimelineBean) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (startTime != other.startTime) - return false; - if (state != other.state) - return false; - if (stopTime != other.stopTime) - return false; - return true; - } - - /** - * NOTE: A {@link ThreadTimelineBean} is contains another if they are - * either equals, or the the name, state and start time are the same - * (in other words, this method does not check the stop time, and is not a - * strict Set operation). - */ - public boolean contains(ThreadTimelineBean other) { - if (equals(other)) { - return true; - } - if (getName().equals(other.getName()) && - getState().equals(other.getState()) && - getStartTime() == other.getStartTime()) - { - return true; - } - - return false; - } - - @Override - public ThreadTimelineBean clone() { - ThreadTimelineBean copy = new ThreadTimelineBean(); - copy.name = this.name; - copy.startTime = this.startTime; - copy.stopTime = this.stopTime; - copy.state = this.state; - return copy; - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/Timeline.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,85 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.common; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; + +public class Timeline implements Iterable<TimelineInfo> { + + private Deque<TimelineInfo> infos; + + private String name; + private long id; + + public Timeline(String name, long id) { + this.name = name; + this.id = id; + infos = new ArrayDeque<>(); + } + + public String getName() { + return name; + } + + public long getId() { + return id; + } + + @Override + public Iterator<TimelineInfo> iterator() { + return infos.descendingIterator(); + } + + public TimelineInfo[] toArray() { + TimelineInfo[] result = new TimelineInfo[size()]; + int i = 0; + for (TimelineInfo info : this) { + result[i++] = info; + } + return result; + } + + public void add(TimelineInfo info) { + infos.add(info); + } + + public int size() { + return infos.size(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/TimelineInfo.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,78 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.common; + +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.storage.model.TimeStampedPojo; + +public class TimelineInfo implements TimeStampedPojo { + + private Palette colour; + private long timeStamp; + + public TimelineInfo() { + timeStamp = 0; + colour = Palette.BLACK; + } + + public TimelineInfo(Palette colour, long timeStamp) { + this.timeStamp = timeStamp; + this.colour = colour; + } + + public Palette getColor() { + return colour; + } + + public void setColor(Palette colour) { + this.colour = colour; + } + + public void setTimeStamp(long timestamp) { + this.timeStamp = timestamp; + } + + @Override + public long getTimeStamp() { + return timeStamp; + } + + @Override + public String toString() { + return "TimelineInfo [colour=" + colour + ", timeStamp=" + timeStamp + "]"; + } +}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/chart/ChartColors.java Thu Dec 20 14:49:16 2012 +0100 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/chart/ChartColors.java Thu Dec 20 19:12:54 2012 +0100 @@ -46,38 +46,42 @@ return getColor(Thread.State.valueOf(state)); } - public static Color getColor(Thread.State state) { - Color result = null; + public static Palette getPaletteColor(Thread.State state) { + Palette result = null; switch (state) { case TIMED_WAITING: - result = Palette.PALE_RED.getColor(); + result = Palette.PALE_RED; break; case NEW: - result = Palette.POMP_AND_POWER_VIOLET.getColor(); + result = Palette.POMP_AND_POWER_VIOLET; break; case RUNNABLE: - result = Palette.PRUSSIAN_BLUE.getColor(); + result = Palette.PRUSSIAN_BLUE; break; case TERMINATED: - result = Palette.GRAY.getColor(); + result = Palette.GRAY; break; case BLOCKED: - result = Palette.RED.getColor(); + result = Palette.RED; break; case WAITING: - result = Palette.GRANITA_ORANGE.getColor(); + result = Palette.GRANITA_ORANGE; break; default: - result = Color.BLACK; + result = Palette.BLACK; break; } return result; } + + public static Color getColor(Thread.State state) { + return getPaletteColor(state).getColor(); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/Page.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,51 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.common.model.timeline; + +import com.redhat.thermostat.common.model.LongRange; + +public class Page { + + private LongRange range; + public Page(LongRange range) { + this.range = range; + } + + public LongRange getSpan() { + return range; + } +}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadTimelineView.java Thu Dec 20 14:49:16 2012 +0100 +++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadTimelineView.java Thu Dec 20 19:12:54 2012 +0100 @@ -37,13 +37,12 @@ package com.redhat.thermostat.thread.client.common.view; import java.util.List; -import java.util.Map; import com.redhat.thermostat.client.core.views.BasicView; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; -import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; -import com.redhat.thermostat.thread.model.ThreadInfoData; +import com.redhat.thermostat.common.model.LongRange; +import com.redhat.thermostat.thread.client.common.Timeline; public abstract class ThreadTimelineView extends BasicView { @@ -64,7 +63,5 @@ threadTimelineNotifier.removeActionListener(listener); } - public abstract void displayStats(Map<ThreadInfoData, List<ThreadTimelineBean>> timelines, long start, long stop); - public abstract void setMarkersMessage(String left, String right); - public abstract void resetMarkerMessage(); + public abstract void displayStats(List<Timeline> timelines, LongRange range); }
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java Thu Dec 20 14:49:16 2012 +0100 +++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java Thu Dec 20 19:12:54 2012 +0100 @@ -36,16 +36,21 @@ package com.redhat.thermostat.thread.client.controller.impl; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; +import com.redhat.thermostat.client.ui.Palette; import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.Timer; -import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; +import com.redhat.thermostat.common.model.LongRange; +import com.redhat.thermostat.thread.client.common.Timeline; +import com.redhat.thermostat.thread.client.common.TimelineInfo; +import com.redhat.thermostat.thread.client.common.chart.ChartColors; import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView; import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView.ThreadTimelineViewAction; @@ -57,7 +62,6 @@ private ThreadCollector collector; private final String lock = new String("ThreadTimelineController"); - private ThreadTimelineBean latestSelected; public ThreadTimelineController(ThreadTimelineView view, ThreadCollector collector, Timer timer) { super(timer, view); @@ -71,96 +75,40 @@ @Override public void actionPerformed(ActionEvent<ThreadTimelineViewAction> actionEvent) { - synchronized (lock) { - latestSelected = (ThreadTimelineBean) actionEvent.getPayload(); - } + // TODO } } private class ThreadTimelineControllerAction implements Runnable { @Override public void run() { - ThreadTimelineBean _latestSelected = null; + synchronized (lock) { - if (latestSelected != null) { - _latestSelected = latestSelected.clone(); - } - } - - List<ThreadInfoData> infos = collector.getThreadInfo(); - if(infos.size() > 0) { - - Map<ThreadInfoData, List<ThreadTimelineBean>> timelines = new HashMap<>(); - - Map<ThreadInfoData, List<ThreadInfoData>> stats = - ThreadInfoHelper.getThreadInfoDataMap(infos); - for (List<ThreadInfoData> beanList : stats.values()) { - - // the list is ordered in most recent first - // the first element is the latest sample we have of this - // thread, so we use it as stop time. - - ThreadInfoData lastThreadInfo = beanList.get(beanList.size() - 1); - Thread.State lastState = lastThreadInfo.getState(); - - ThreadTimelineBean timeline = new ThreadTimelineBean(); - timeline.setName(lastThreadInfo.getThreadName()); - timeline.setState(lastThreadInfo.getState()); - timeline.setStartTime(lastThreadInfo.getTimeStamp()); - - long stopTime = beanList.get(0).getTimeStamp(); - timeline.setStopTime(stopTime); - - Stack<ThreadTimelineBean> threadTimelines = new Stack<ThreadTimelineBean>(); - timelines.put(lastThreadInfo, threadTimelines); - - if (_latestSelected != null && _latestSelected.contains(timeline)) { - timeline.setHighlight(true); - } - threadTimelines.push(timeline); - - for (int i = beanList.size() - 1; i >= 0; i--) { - ThreadInfoData threadInfo = beanList.get(i); - - Thread.State currentState = threadInfo.getState(); - if (currentState != lastState) { - lastState = currentState; - - timeline = threadTimelines.pop(); - timeline.setStopTime(threadInfo.getTimeStamp()); - - if (_latestSelected != null && _latestSelected.contains(timeline)) { - timeline.setHighlight(true); + // FIXME: only load latest, not all the info all the time + LongRange range = new LongRange(Long.MAX_VALUE, Long.MIN_VALUE); + List<ThreadInfoData> infos = collector.getThreadInfo(); + if(infos.size() > 0) { + Map<ThreadInfoData, List<ThreadInfoData>> stats = ThreadInfoHelper.getThreadInfoDataMap(infos); + List<Timeline> timelines = new ArrayList<>(); + for (List<ThreadInfoData> beanList : stats.values()) { + Timeline timeline = new Timeline(beanList.get(0).getThreadName(), beanList.get(0).getThreadId()); + + for (ThreadInfoData data : beanList) { + Palette palette = ChartColors.getPaletteColor(data.getState()); + long timestamp = data.getTimeStamp(); + TimelineInfo info = new TimelineInfo(palette, timestamp); + timeline.add(info); + + if (range.getMin() > timestamp) { + range.setMin(timestamp); } - - threadTimelines.push(timeline); - - timeline = new ThreadTimelineBean(); - timeline.setName(threadInfo.getThreadName()); - timeline.setState(threadInfo.getState()); - timeline.setStartTime(threadInfo.getTimeStamp()); - timeline.setStopTime(stopTime); - - lastThreadInfo = threadInfo; - lastState = currentState; - - if (_latestSelected != null && _latestSelected.contains(timeline)) { - timeline.setHighlight(true); + if (range.getMax() < timestamp) { + range.setMax(timestamp); } - - // add the new thread stat - threadTimelines.push(timeline); } + timelines.add(timeline); } - } - - view.displayStats(timelines, infos.get(infos.size() - 1).getTimeStamp(), infos.get(0).getTimeStamp()); - - if (_latestSelected != null) { - view.setMarkersMessage(new Date(_latestSelected.getStartTime()).toString(), - new Date(_latestSelected.getStopTime()).toString()); - } else { - view.resetMarkerMessage(); + view.displayStats(timelines, range); } } }
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.java Thu Dec 20 14:49:16 2012 +0100 +++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.java Thu Dec 20 19:12:54 2012 +0100 @@ -55,7 +55,11 @@ import com.redhat.thermostat.common.ActionEvent; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.Timer; -import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; +import com.redhat.thermostat.common.model.LongRange; +import com.redhat.thermostat.thread.client.common.Timeline; +import com.redhat.thermostat.thread.client.common.TimelineInfo; +import com.redhat.thermostat.thread.client.common.chart.ChartColors; +//import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; import com.redhat.thermostat.thread.client.common.collector.ThreadCollector; import com.redhat.thermostat.thread.client.common.view.ThreadTableView; import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView; @@ -140,32 +144,36 @@ Runnable action = timerCaptor.getValue(); action.run(); - ArgumentCaptor<Map> mapCaptor = ArgumentCaptor.forClass(Map.class); - - verify(view).displayStats(mapCaptor.capture(), anyLong(), anyLong()); - - Map viewResult = mapCaptor.getValue(); - assertEquals(2, viewResult.size()); - - List<ThreadTimelineBean> beanList = (List<ThreadTimelineBean>) viewResult.get(data1); - - assertEquals(2, beanList.size()); + ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor<LongRange> rangeCaptor = ArgumentCaptor.forClass(LongRange.class); + + verify(view).displayStats(listCaptor.capture(), rangeCaptor.capture()); - assertEquals("test1", beanList.get(0).getName()); - assertEquals("test1", beanList.get(1).getName()); + List viewResult = listCaptor.getValue(); + LongRange rangeResult = rangeCaptor.getValue(); + assertEquals(2, viewResult.size()); + assertEquals(100, rangeResult.getMin()); + assertEquals(3000, rangeResult.getMax()); - beanList = (List<ThreadTimelineBean>) viewResult.get(data2); - assertEquals(2, beanList.size()); + Timeline timeline = (Timeline) viewResult.get(0); + assertEquals(2, timeline.getId()); + assertEquals("test2", timeline.getName()); + assertEquals(3, timeline.size()); - assertEquals("test2", beanList.get(0).getName()); - assertEquals("test2", beanList.get(1).getName()); + TimelineInfo [] timelineInfos = timeline.toArray(); + assertEquals(ChartColors.getPaletteColor(data2.getState()), timelineInfos[0].getColor()); + assertEquals(ChartColors.getPaletteColor(data4.getState()), timelineInfos[1].getColor()); + assertEquals(ChartColors.getPaletteColor(data5.getState()), timelineInfos[2].getColor()); - assertEquals(1000, beanList.get(0).getStartTime()); - assertEquals(3000, beanList.get(0).getStopTime()); - assertEquals(Thread.State.BLOCKED, beanList.get(0).getState()); - - assertEquals(3000, beanList.get(1).getStartTime()); - assertEquals(Thread.State.BLOCKED, beanList.get(0).getState()); + + timeline = (Timeline) viewResult.get(1); + assertEquals(1, timeline.getId()); + assertEquals("test1", timeline.getName()); + assertEquals(2, timeline.size()); + + timelineInfos = timeline.toArray(); + assertEquals(ChartColors.getPaletteColor(data1.getState()), timelineInfos[0].getColor()); + assertEquals(ChartColors.getPaletteColor(data3.getState()), timelineInfos[1].getColor()); } @Test
--- a/thread/client-swing/pom.xml Thu Dec 20 14:49:16 2012 +0100 +++ b/thread/client-swing/pom.xml Thu Dec 20 19:12:54 2012 +0100 @@ -115,6 +115,7 @@ <Private-Package> com.redhat.thermostat.thread.client.swing.osgi, com.redhat.thermostat.thread.client.swing.impl, + com.redhat.thermostat.thread.client.swing.impl.timeline, </Private-Package> <!-- Do not autogenerate uses clauses in Manifests --> <_nouses>true</_nouses>
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineChart.java Thu Dec 20 14:49:16 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,256 +0,0 @@ -/* - * Copyright 2012 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.thread.client.swing.impl; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.GradientPaint; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; - -import com.redhat.thermostat.client.swing.GraphicsUtils; -import com.redhat.thermostat.client.swing.components.GradientRoundBorder; -import com.redhat.thermostat.client.ui.Palette; -import com.redhat.thermostat.common.model.LongRange; -import com.redhat.thermostat.common.model.LongRangeNormalizer; -import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; -import com.redhat.thermostat.thread.client.common.chart.ChartColors; - -@SuppressWarnings("serial") -public class SwingThreadTimelineChart extends JPanel { - - private String leftMarkerMessage; - private String rightMarkerMessage; - - public static final String HIGHLIGHT_THREAD_STATE_PROPERTY = "highlightThreadThreadProperty"; - private ThreadTimelineBean selectedThread; - - private List<ThreadTimelineBean> timeline; - - private LongRangeNormalizer normalizer; - - private Point clickArea; - - public SwingThreadTimelineChart(List<ThreadTimelineBean> timeline, long rangeStart, long rangeStop) { - this.timeline = timeline; - - LongRange range = new LongRange(); - range.setMin(rangeStart); - range.setMax(rangeStop); - - setBorder(new GradientRoundBorder()); - normalizer = new LongRangeNormalizer(range); - } - - public void clickAndHighlightArea(Point point) { - clickArea = point; - repaint(); - } - - public void unsetHighlightArea() { - clickArea = null; - selectedThread = null; - repaint(); - } - - @Override - protected void paintComponent(Graphics g) { - - ThreadTimelineBean oldSelectedThread = selectedThread; - - normalizer.setMinNormalized(0); - normalizer.setMaxNormalized(getWidth() - 3); - - GraphicsUtils utils = GraphicsUtils.getInstance(); - - Graphics2D graphics = utils.createAAGraphics(g); - - Font font = graphics.getFont(); - graphics.setFont(font.deriveFont(font.getSize() - 2)); - - int y = getHeight()/3; - graphics.clearRect(0, 0, getWidth(), getHeight()); - graphics.drawString(timeline.get(0).getName(), 2, y); - - y = getHeight()/2; - - graphics.translate(2, y); - if (clickArea != null) { - clickArea.translate(0, -y); - } - - int w = getWidth() - 4; - int h = 15; - - Shape shape = utils.getRoundShape(w, h); - - Paint paint = new GradientPaint(0, 0, Palette.DARK_GRAY.getColor(), 0, h, Palette.WHITE.getColor()); - graphics.setPaint(paint); - graphics.fill(shape); - - - for (ThreadTimelineBean thread : timeline) { - - boolean isSelected = false; - normalizer.setValue(thread.getStartTime()); - int x0 = (int) normalizer.getValueNormalized(); - - normalizer.setValue(thread.getStopTime()); - int x1 = (int) normalizer.getValueNormalized(); - - Color currentThreadColour = ChartColors.getColor(thread.getState()); - Rectangle currentArea = new Rectangle(x0, 0, x1 - x0, h); - if (clickArea != null && currentArea.contains(clickArea)) { - currentThreadColour = Palette.THERMOSTAT_BLU.getColor(); - selectedThread = thread; - isSelected = true; - - } else if (thread.isHighlight()) { - currentThreadColour = Palette.THERMOSTAT_BLU.getColor(); - isSelected = true; - } - - graphics.setColor(currentThreadColour); - graphics.fillRect(x0, 1, x1 - x0, h - 1); - - if (isSelected) { - paintMarks(graphics, x0, x1, h, thread.getStartTime(), thread.getStopTime(), ChartColors.getColor(thread.getState())); - String tooltipString = ""; - if (leftMarkerMessage != null) { - tooltipString = leftMarkerMessage + " - "; - } - if (rightMarkerMessage != null) { - tooltipString += " " + rightMarkerMessage; - } - setToolTipText(tooltipString); - } - } - - graphics.setColor(ChartColors.getColor(timeline.get(timeline.size() - 1).getState())); - graphics.draw(shape); - - graphics.dispose(); - - if (selectedThread != null || oldSelectedThread != null) { - firePropertyChange(HIGHLIGHT_THREAD_STATE_PROPERTY, oldSelectedThread, selectedThread); - } - } - - private void paintMarks(Graphics2D graphics, int x0, int x1, int y, long start, long stop, Color colour) { - - graphics.setColor(colour); - graphics.fillRect(x0, y + 5, x1 - x0, 2); - - GraphicsUtils utils = GraphicsUtils.getInstance(); - FontMetrics metrics = utils.getFontMetrics(this, graphics.getFont()); - - graphics.setColor(getForeground()); - if (leftMarkerMessage != null) { - Rectangle2D rect = metrics.getStringBounds(leftMarkerMessage, graphics); - graphics.drawString(leftMarkerMessage, x0 - (int) rect.getWidth(), y - (int) rect.getY()); - } - - if (rightMarkerMessage != null) { - Rectangle2D rect = metrics.getStringBounds(leftMarkerMessage, graphics); - graphics.drawString(rightMarkerMessage, x1, y - (int) rect.getY()); - } - } - - public static void main(String[] args) { - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - ThreadTimelineBean bean1 = new ThreadTimelineBean(); - bean1.setName("test"); - bean1.setStartTime(0); - bean1.setStopTime(1000); - bean1.setState(Thread.State.BLOCKED); - - ThreadTimelineBean bean2 = new ThreadTimelineBean(); - bean2.setName("test"); - bean2.setStartTime(1000); - bean2.setStopTime(2000); - bean2.setState(Thread.State.RUNNABLE); - - ThreadTimelineBean bean3 = new ThreadTimelineBean(); - bean3.setName("test"); - bean3.setStartTime(2000); - bean3.setStopTime(2100); - bean3.setState(Thread.State.TIMED_WAITING); - - List<ThreadTimelineBean> timeline = new ArrayList<>(); - timeline.add(bean1); - timeline.add(bean2); - timeline.add(bean3); - - bean3.setHighlight(true); - - SwingThreadTimelineChart chart = new SwingThreadTimelineChart(timeline, 0, 2500); - chart.setMarkersMessage(new Date(bean3.getStartTime()).toString(), new Date(bean3.getStopTime()).toString()); - - frame.add(chart); - frame.setSize(800, 150); - - frame.setVisible(true); - } - }); - } - - public void setMarkersMessage(String leftMarkerMessage, String rightMarkerMessage) { - this.leftMarkerMessage = leftMarkerMessage; - this.rightMarkerMessage = rightMarkerMessage; - } -}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Thu Dec 20 14:49:16 2012 +0100 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java Thu Dec 20 19:12:54 2012 +0100 @@ -55,23 +55,32 @@ import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.client.ui.ComponentVisibleListener; -import com.redhat.thermostat.thread.client.common.ThreadTimelineBean; +import com.redhat.thermostat.common.model.LongRange; + +import com.redhat.thermostat.thread.client.common.Timeline; +import com.redhat.thermostat.thread.client.common.TimelineInfo; import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView; +import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineCellRenderer; +import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineComponent; +import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineRulerHeader; +import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineUtils; import com.redhat.thermostat.thread.model.ThreadInfoData; -public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent { +public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent { private final String lock = new String("SwingThreadTimelineViewLock"); private JPanel timeLinePanel; - private JList<SwingThreadTimelineChart> chartList; - private DefaultListModel<SwingThreadTimelineChart> chartModel; + private JList<TimelineComponent> chartList; + private DefaultListModel<TimelineComponent> chartModel; - private String leftMarkerMessage; - private String rightMarkerMessage; + private TimelineRulerHeader header; + private JScrollPane scrollPane; public SwingThreadTimelineView() { timeLinePanel = new JPanel(); @@ -89,117 +98,74 @@ timeLinePanel.setLayout(new BorderLayout(0, 0)); - chartModel = new DefaultListModel<>(); chartList = new JList<>(chartModel); - chartList.setCellRenderer(new ChartRenderer()); - chartList.addMouseListener(new ChartListListener()); + chartList.setCellRenderer(new TimelineCellRenderer()); - JScrollPane scrollPane = new JScrollPane(chartList); - scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + JScrollPane scrollPane = createScrollPane(); timeLinePanel.add(scrollPane, BorderLayout.CENTER); ThreadTimelineLegendPanel timelineLegend = new ThreadTimelineLegendPanel(); timeLinePanel.add(timelineLegend, BorderLayout.SOUTH); } - @Override - public void displayStats(final Map<ThreadInfoData, List<ThreadTimelineBean>> timelines, final long start, final long stop) { + private JScrollPane createScrollPane() { + scrollPane = new JScrollPane(chartList); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + long now = System.currentTimeMillis(); + header = new TimelineRulerHeader(new LongRange(now, now + TimelineUtils.STEP), scrollPane); + scrollPane.setColumnHeaderView(header); + scrollPane.getHorizontalScrollBar().getModel().addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + scrollPane.repaint(); + } + }); + scrollPane.getVerticalScrollBar().getModel().addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + scrollPane.repaint(); + } + }); + return scrollPane; + } + + @Override + public void displayStats(final List<Timeline> timelines, final LongRange range) { SwingUtilities.invokeLater(new Runnable() { - @Override public void run() { - String _leftMarkerMessage = null; - String _rightMarkerMessage = null; - synchronized (lock) { - _leftMarkerMessage = leftMarkerMessage; - _rightMarkerMessage = rightMarkerMessage; + chartModel.removeAllElements(); + for (Timeline timeline : timelines) { + chartModel.addElement(new TimelineComponent(range, timeline, scrollPane)); } - - chartModel.clear(); - for (List<ThreadTimelineBean> timeline : timelines.values()) { - SwingThreadTimelineChart panel = new SwingThreadTimelineChart(timeline, start, stop); - panel.setPreferredSize(new Dimension(chartList.getWidth(), 75)); - panel.setMarkersMessage(_leftMarkerMessage, _rightMarkerMessage); - panel.addPropertyChangeListener(SwingThreadTimelineChart.HIGHLIGHT_THREAD_STATE_PROPERTY, - new SelectedThreadListener()); - panel.setMarkersMessage(_leftMarkerMessage, _rightMarkerMessage); - chartModel.addElement(panel); - } + header.getRange().setMin(range.getMin()); + header.getRange().setMax(range.getMax()); } }); } - private class SelectedThreadListener implements PropertyChangeListener { - @Override - public void propertyChange(final PropertyChangeEvent evt) { - SwingWorker<Void, Void> notifier = new SwingWorker<Void, Void>() { - @Override - protected Void doInBackground() throws Exception { - SwingThreadTimelineView.this. - threadTimelineNotifier.fireAction(ThreadTimelineView.ThreadTimelineViewAction.THREAD_TIMELINE_SELECTED, - evt.getNewValue()); - return null; - } - }; - notifier.execute(); - } - } - - private class ChartRenderer implements ListCellRenderer<SwingThreadTimelineChart> { - @Override - public Component getListCellRendererComponent(JList<? extends SwingThreadTimelineChart> list, - SwingThreadTimelineChart chart, - int index, boolean isSelected, - boolean cellHasFocus) - { - if (!isSelected) { - chart.unsetHighlightArea(); - } - return chart; - } - } - - private class ChartListListener extends MouseAdapter { - - @Override - public void mouseClicked(MouseEvent e) { - int index = chartList.getSelectedIndex(); - if (index > 0) { - Point listIndex = chartList.indexToLocation(index); - Point absoluteLocation = e.getPoint(); - listIndex.x = absoluteLocation.x; - if (index != 0) { - listIndex.y = absoluteLocation.y - listIndex.y; - } - - SwingThreadTimelineChart chart = chartModel.get(index); - chart.clickAndHighlightArea(listIndex); - } - } - } +// private class SelectedThreadListener implements PropertyChangeListener { +// @Override +// public void propertyChange(final PropertyChangeEvent evt) { +// SwingWorker<Void, Void> notifier = new SwingWorker<Void, Void>() { +// @Override +// protected Void doInBackground() throws Exception { +// SwingThreadTimelineView.this. +// threadTimelineNotifier.fireAction(ThreadTimelineView.ThreadTimelineViewAction.THREAD_TIMELINE_SELECTED, +// evt.getNewValue()); +// return null; +// } +// }; +// notifier.execute(); +// } +// } @Override public Component getUiComponent() { return timeLinePanel; } - - @Override - public void resetMarkerMessage() { - synchronized (lock) { - this.leftMarkerMessage = null; - this.rightMarkerMessage = null; - } - } - - @Override - public void setMarkersMessage(String left, String right) { - synchronized (lock) { - this.leftMarkerMessage = left; - this.rightMarkerMessage = right; - } - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineCellRenderer.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,55 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.swing.impl.timeline; + +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; + +@SuppressWarnings("serial") +public class TimelineCellRenderer extends JPanel implements ListCellRenderer<TimelineComponent> { + + @Override + public TimelineComponent getListCellRendererComponent(JList<? extends TimelineComponent> list, + TimelineComponent value, + int index, boolean isSelected, + boolean cellHasFocus) + { + value.setSelected(isSelected); + return value; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineComponent.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,273 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.swing.impl.timeline; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Rectangle; +import java.beans.Transient; + +import javax.swing.DefaultListModel; +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import com.redhat.thermostat.client.swing.GraphicsUtils; +import com.redhat.thermostat.client.swing.components.GradientPanel; +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.common.model.LongRange; +import com.redhat.thermostat.common.model.LongRangeNormalizer; +import com.redhat.thermostat.thread.client.common.Timeline; +import com.redhat.thermostat.thread.client.common.TimelineInfo; + +@SuppressWarnings("serial") +public class TimelineComponent extends GradientPanel { + + private boolean selected = false; + + private Timeline timeline; + private JScrollPane scrollPane; + private LongRange range; + public TimelineComponent(LongRange range, Timeline timeline, JScrollPane scrollPane) { + super(Palette.LIGHT_GRAY.getColor(), Palette.WHITE.getColor()); + this.range = range; + this.scrollPane = scrollPane; + this.timeline = timeline; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + @Override + protected void paintComponent(Graphics g) { + + Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); + Rectangle bounds = g.getClipBounds(); + + if (!selected) { + super.paintComponent(g); + } else { + graphics.setColor(Palette.EGYPTIAN_BLUE.getColor()); + graphics.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + } + + int height = getHeight(); + int currentValue = scrollPane.getHorizontalScrollBar().getValue(); + int totalInc = TimelineUtils.drawMarks(range, graphics, bounds, currentValue, getWidth(), height); + + drawBoldMarks(graphics, currentValue, bounds, totalInc); + Color lastColor = drawTimeline(graphics, currentValue, bounds); + + drawThreadName(graphics, bounds, lastColor); + + graphics.dispose(); + } + + private void drawThreadName(Graphics2D graphics, Rectangle bounds, Color lastColor) { + GraphicsUtils utils = GraphicsUtils.getInstance(); + + Color up = utils.deriveWithAlpha(Palette.WHITE.getColor(), 200); + Color bottom = utils.deriveWithAlpha(Palette.GRAY.getColor(), 200); + Paint gradient = new GradientPaint(0, 0, up, 0, getHeight(), bottom); + + Font font = TimelineUtils.FONT; + + graphics.setFont(font); + graphics.setPaint(gradient); + + String value = timeline.getName(); + + int stringWidth = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getWidth() - 1; + int stringHeight = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getHeight(); + graphics.fillRect(bounds.x + 1, bounds.y + 12, stringWidth + 4, stringHeight + 4); + + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + graphics.drawString(value, bounds.x + 1, bounds.y + stringHeight + 12); + + graphics.setColor(lastColor); + graphics.drawLine(bounds.x + 1, bounds.y + 12 + stringHeight + 4, bounds.x + stringWidth + 4, bounds.y + 12 + stringHeight + 4); + } + + private Color drawTimeline(Graphics2D graphics, int currentValue, Rectangle bounds) { + + if (timeline.size() == 0) { + return Palette.GRAY.getColor(); + } + + TimelineInfo[] infos = timeline.toArray(); + Color lastColor = infos[infos.length - 1].getColor().getColor(); + + LongRangeNormalizer normalizer = new LongRangeNormalizer(range, 0, getWidth()); + + for (int i = 0; i < infos.length - 1; i++) { + TimelineInfo info1 = infos[i]; + TimelineInfo info2 = infos[i + 1]; + + normalizer.setValue(info1.getTimeStamp()); + int x0 = (int) normalizer.getValueNormalized(); + + normalizer.setValue(info2.getTimeStamp()); + int x1 = (int) normalizer.getValueNormalized(); + + graphics.setColor(info1.getColor().getColor()); + graphics.fillRect(x0, 5, x1 - x0 + 1, 5); + } + + normalizer.setValue(infos[infos.length - 1].getTimeStamp()); + int x0 = (int) normalizer.getValueNormalized(); + + normalizer.setValue(infos[infos.length - 1].getTimeStamp() + 250); + int x1 = (int) normalizer.getValueNormalized(); + + graphics.setColor(lastColor); + graphics.fillRect(x0, 5, x1 - x0 + 1, 5); + + return lastColor; + } + + private void drawBoldMarks(Graphics2D graphics, int currentValue, Rectangle bounds, int totalInc) { + + long round = range.getMin() % 10000; + int shift = (int) (round / TimelineUtils.STEP) * totalInc; + + int lowerBound = bounds.x - (4 * totalInc); + int x = ((bounds.x - currentValue) - shift); + + int increment = 0; + int height = getHeight(); + + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + int upperBound = (bounds.x + bounds.width); + for (int i = x; i < upperBound; i += totalInc) { + if (increment % 10 == 0 && (i >= lowerBound)) { + graphics.drawLine(i, 0, i, height); + } + increment++; + } + } + + public LongRange getRange() { + return range; + } + + @Override + public int getHeight() { + return 40; + } + + @Override + public int getWidth() { + return TimelineUtils.calculateWidth(range); + } + + @Override + public Dimension getSize() { + return getPreferredSize(); + } + + @Override + @Transient + public Dimension getPreferredSize() { + return new Dimension(getWidth(), getHeight()); + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + + LongRange range = new LongRange(50000, 2000000); // 31558464000L + Timeline timeline = new Timeline("Test", 1000); + timeline.add(new TimelineInfo(Palette.THERMOSTAT_BLU, range.getMax() - 1000)); + timeline.add(new TimelineInfo(Palette.TUNDRA_GREEN, 152000)); + timeline.add(new TimelineInfo(Palette.DIRTY_CYAN, 63000)); + timeline.add(new TimelineInfo(Palette.THERMOSTAT_BLU, 62000)); + timeline.add(new TimelineInfo(Palette.THERMOSTAT_RED, 60210)); + timeline.add(new TimelineInfo(Palette.THERMOSTAT_BLU, 51299)); + + final JFrame frame = new JFrame(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + + final JScrollPane scrollPane = new JScrollPane(); + DefaultListModel<TimelineComponent> chartModel = new DefaultListModel<>(); + for (int i = 0; i < 100; i++) { + chartModel.addElement(new TimelineComponent(range, timeline, scrollPane)); + } + + final JList<TimelineComponent> stuff = new JList<>(chartModel); + stuff.setCellRenderer(new TimelineCellRenderer()); + + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.getHorizontalScrollBar().setUnitIncrement(TimelineUtils.INC); + + final TimelineRulerHeader header = new TimelineRulerHeader(range, scrollPane); + + scrollPane.setColumnHeaderView(header); + scrollPane.setViewportView(stuff); + scrollPane.getVerticalScrollBar().getModel().addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + scrollPane.repaint(); + } + }); + + scrollPane.getHorizontalScrollBar().getModel().addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + scrollPane.repaint(); + } + }); + + frame.add(scrollPane); + frame.setSize(300, 300); + frame.setVisible(true); + } + }); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineRulerHeader.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,157 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.swing.impl.timeline; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Rectangle; +import java.beans.Transient; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.swing.JScrollPane; + +import com.redhat.thermostat.client.swing.GraphicsUtils; +import com.redhat.thermostat.client.swing.components.GradientPanel; +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.common.model.LongRange; + +@SuppressWarnings("serial") +public class TimelineRulerHeader extends GradientPanel { + + private LongRange range; + private JScrollPane scrollPane; + + public TimelineRulerHeader(LongRange range, JScrollPane scrollPane) { + + super(Palette.LIGHT_GRAY.getColor(), Palette.WHITE.getColor()); + + this.range = range; + this.scrollPane = scrollPane; + } + + public LongRange getRange() { + return range; + } + + @Override + public int getHeight() { + return 25; + } + + @Override + @Transient + public Dimension getPreferredSize() { + Dimension dim = super.getPreferredSize(); + dim.height = getHeight(); + return dim; + } + + @Override + public Dimension getSize() { + return getPreferredSize(); + } + + @Override + protected void paintComponent(Graphics g) { + + super.paintComponent(g); + + Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g); + + int currentValue = scrollPane.getHorizontalScrollBar().getValue(); + + Rectangle bounds = g.getClipBounds(); + int totalInc = TimelineUtils.drawMarks(range, graphics, bounds, currentValue, + TimelineUtils.calculateWidth(range), + getHeight(), true); + + drawTimelineStrings(graphics, currentValue, bounds, totalInc); + + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + graphics.drawLine(bounds.x, bounds.height - 1, bounds.width, bounds.height - 1); + + graphics.dispose(); + } + + private void drawTimelineStrings(Graphics2D graphics, int currentValue, Rectangle bounds, int totalInc) { + + Font font = TimelineUtils.FONT; + + graphics.setFont(font); + + DateFormat df = new SimpleDateFormat("HH:mm:ss"); + + Paint gradient = new GradientPaint(0, 0, Palette.WHITE.getColor(), 0, getHeight(), Palette.GRAY.getColor()); + + graphics.setColor(Palette.EARL_GRAY.getColor()); + + long round = range.getMin() % (TimelineUtils.STEP * 10); + int shift = (int) (round / TimelineUtils.STEP) * totalInc; + long currentTime = range.getMin() - round; + + int lowerBound = bounds.x - (4 * totalInc); + int x = ((bounds.x - currentValue) - shift); + + long increment = 0; + int height = getHeight(); + for (int i = x; i < bounds.width; i += totalInc) { + if (increment % 10 == 0 && i >= lowerBound) { + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + graphics.drawLine(i, 0, i, height); + + graphics.setPaint(gradient); + + String value = df.format(new Date(currentTime)); + + int stringWidth = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getWidth() - 1; + int stringHeight = (int) font.getStringBounds(value, graphics.getFontRenderContext()).getHeight(); + graphics.fillRect(i + 1, bounds.y + 5, stringWidth + 4, stringHeight + 4); + + graphics.setColor(Palette.THERMOSTAT_BLU.getColor()); + graphics.drawString(value, i + 1, bounds.y + stringHeight + 5); + } + currentTime += TimelineUtils.STEP; + increment++; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineUtils.java Thu Dec 20 19:12:54 2012 +0100 @@ -0,0 +1,85 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.thread.client.swing.impl.timeline; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Rectangle; + +import com.redhat.thermostat.client.ui.Palette; +import com.redhat.thermostat.common.model.LongRange; +import com.redhat.thermostat.common.model.LongRangeNormalizer; + + +public class TimelineUtils { + public static final int INC = 50; + public static final int STEP = 1000; + public static final Font FONT = new Font("SansSerif", Font.PLAIN, 10); + + public static int calculateWidth(LongRange range) { + long span = range.getMax() - range.getMin(); + int width = (int) (span / TimelineUtils.INC); + return width; + } + + public static int drawMarks(LongRange range, Graphics2D graphics, Rectangle bounds, int currentValue, int width, int height) { + return drawMarks(range, graphics, bounds, currentValue, width, height, false); + } + + public static int drawMarks(LongRange range, Graphics2D graphics, Rectangle bounds, int currentValue, int width, int height, boolean darkerTop) { + LongRangeNormalizer normalizer = new LongRangeNormalizer(range, 0, width); + normalizer.setValue(range.getMin() + TimelineUtils.STEP); + int totalInc = (int) normalizer.getValueNormalized(); + + int inc = currentValue % totalInc; + int x = (bounds.x - inc); + + graphics.setColor(Palette.GRAY.getColor()); + int upperBound = (bounds.x + bounds.width); + + for (int i = x; i < upperBound; i += totalInc) { + graphics.drawLine(i, 0, i, height); + if (darkerTop) { + graphics.setColor(Palette.DARK_GRAY.getColor()); + graphics.drawLine(i, 0, i, 5); + graphics.setColor(Palette.GRAY.getColor()); + } + } + + return totalInc; + } +}