blob: 1a6266a23254edb6703d8000f1ffc243e1b3cd39 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2015 Tilera Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* William R. Swanson (Tilera Corporation)
* Marc Dumais (Ericsson) - Bug 458671
*******************************************************************************/
package org.eclipse.cdt.visualizer.examples.sourcegraph;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import org.eclipse.cdt.visualizer.examples.VisualizerExamplesPlugin;
import org.eclipse.cdt.visualizer.ui.canvas.BufferedCanvas;
import org.eclipse.cdt.visualizer.ui.canvas.GraphicObject;
import org.eclipse.cdt.visualizer.ui.util.Colors;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.osgi.framework.Bundle;
// ---------------------------------------------------------------------------
// SourceGraphControl
// ---------------------------------------------------------------------------
/** Simple control that displays a graph based on a source text selection. */
public class SourceGraphControl extends BufferedCanvas {
// --- constants ---
/** Margin used in drawing graph and computing control height. */
public static final int MARGIN = 10;
/** Line height used in drawing graph and computing control height. */
public static final int LINE_HEIGHT = 20;
/** Path for the banner image, relative to plug-in root path */
protected static final String BANNER_FILE = "images/sgv-banner.png"; //$NON-NLS-1$
protected static final int BANNER_HEIGHT = 50;
// --- members ---
/** Text we're currently displaying. */
protected String m_sourceText = ""; //$NON-NLS-1$
protected GraphicObject m_banner;
/** Data structure used to hold character stats. */
class CharStat implements Comparable<CharStat> {
public String characters;
public int count;
public CharStat(String c) {
characters = c;
count = 0;
}
@Override
public int compareTo(CharStat o) {
int c1 = count;
int c2 = o.count;
int cmp = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
// we want to sort in descending order, so negate result
return -cmp;
}
}
/** List of characters we discovered and their occurrences. */
ArrayList<CharStat> m_characters;
// --- constructors/destructors ---
/** Constructor. */
public SourceGraphControl(Composite parent) {
super(parent);
m_characters = new ArrayList<>();
m_banner = new GraphicObject();
}
/** Dispose method. */
@Override
public void dispose() {
super.dispose();
}
// --- accessors ---
/** Sets source text to graph. */
public void setSourceText(String text) {
processText(text);
SourceGraphControl.this.update();
}
// --- text processing methods ---
/** Processes text into digested display form. */
public void processText(String text) {
if (text == null)
text = ""; //$NON-NLS-1$
m_sourceText = text;
// TODO: reuse the array/hashtable and stat objects
Hashtable<String, CharStat> characters = new Hashtable<>();
int len = m_sourceText.length();
int fragment_length = 2;
if (len >= fragment_length) {
for (int i = 0; i < len - fragment_length + 1; ++i) {
String c = m_sourceText.substring(i, i + fragment_length);
// Don't bother with fragments containing spaces
// and non-printing chars.
boolean skip = false;
for (int j = 0; j < c.length(); ++j) {
if (c.charAt(j) <= 32 || c.charAt(j) > 127) {
skip = true;
break;
}
}
if (skip)
continue;
CharStat cs = characters.get(c);
if (cs == null) {
cs = new CharStat(c);
characters.put(c, cs);
}
++cs.count;
}
}
m_characters.clear();
m_characters.addAll(characters.values());
Collections.sort(m_characters);
characters.clear();
Rectangle bounds = getBounds();
int height = MARGIN * 2 + m_characters.size() * LINE_HEIGHT;
// reserve space for banner at the top
height = height + BANNER_HEIGHT;
bounds.height = height;
setBounds(bounds);
}
// --- painting methods ---
/** Invoked when canvas repaint event is raised.
* Default implementation clears canvas to background color.
*/
@Override
public void paintCanvas(GC gc) {
gc.setBackground(Colors.BLACK);
gc.setForeground(Colors.WHITE);
clearCanvas(gc);
int margin = MARGIN;
int tw = 90;
int tw2 = 45;
int lh = LINE_HEIGHT;
int x = margin;
// skip banner space
int y = BANNER_HEIGHT + margin;
Rectangle area = getClientArea();
int w = area.width - margin * 2 - tw;
// position and size banner container object
m_banner.setBounds(new Rectangle(x - margin, margin, area.width, BANNER_HEIGHT - margin));
// draw banner
try {
m_banner.drawImage(gc, getAbsFilePath(BANNER_FILE), GraphicObject.ImageSizeAndPosition.MAXSIZE);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
int maxcount = 0;
for (CharStat cs : m_characters) {
// We're sorted in descending order, so first element
// that we draw will always have the largest count.
if (maxcount == 0)
maxcount = cs.count;
gc.drawText("[" + cs.characters + "]", x, y); //$NON-NLS-1$ //$NON-NLS-2$
gc.drawText("(" + cs.count + ")", x + tw2, y); //$NON-NLS-1$ //$NON-NLS-2$
double proportion = cs.count * 1.0 / maxcount;
int bw = (int) Math.round(proportion * w);
Color oldb = gc.getBackground();
if (proportion > .70)
gc.setBackground(Colors.GREEN);
else if (proportion > .40)
gc.setBackground(Colors.YELLOW);
else
gc.setBackground(Colors.RED);
gc.fillRectangle(x + tw, y, bw, lh - 5);
gc.setBackground(oldb);
y += lh;
}
}
// --- update methods ---
/**
* Refresh the control's content based on current data.
*/
public void refresh() {
SourceGraphControl.this.update();
}
// --- event handlers ---
/** Invoked when panel is resized. */
@Override
public void resized(Rectangle bounds) {
refresh();
}
/** Get the absolute path of a file, from the path relative to plugin root. */
private String getAbsFilePath(String relPath) {
Bundle bundle = Platform.getBundle(VisualizerExamplesPlugin.PLUGIN_ID);
URL fileURL = bundle.getEntry(relPath);
File file = null;
try {
file = new File(FileLocator.resolve(fileURL).toURI());
} catch (URISyntaxException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return file.getAbsolutePath();
}
}