--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.peccommon/src/com/nokia/carbide/cpp/pi/peccommon/PecCommonTraceGraph.java Wed Apr 21 15:14:16 2010 +0300
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:
+ *
+ */
+
+package com.nokia.carbide.cpp.pi.peccommon;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Vector;
+
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.FigureCanvas;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.Panel;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.jface.action.Action;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+
+import com.nokia.carbide.cpp.internal.pi.analyser.NpiInstanceRepository;
+import com.nokia.carbide.cpp.internal.pi.analyser.ProfileVisualiser;
+import com.nokia.carbide.cpp.internal.pi.model.GenericSample;
+import com.nokia.carbide.cpp.internal.pi.plugin.model.ITitleBarMenu;
+import com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph;
+import com.nokia.carbide.cpp.internal.pi.visual.GraphComposite;
+import com.nokia.carbide.cpp.internal.pi.visual.PIEvent;
+import com.nokia.carbide.cpp.internal.pi.visual.PIEventListener;
+import com.nokia.carbide.cpp.pi.editors.PIPageEditor;
+import com.nokia.carbide.cpp.pi.util.ColorPalette;
+import com.nokia.carbide.cpp.pi.visual.IGenericTraceGraph;
+
+/**
+ * The performance counter graph class, responsible for drawing the several graphs
+ * based on performance counters as well as managing the legend views to some extend
+ */
+public class PecCommonTraceGraph extends GenericTraceGraph implements
+ MouseMotionListener, PIEventListener, ITitleBarMenu {
+
+ private static int X_LEGEND_HEIGHT = 20;
+
+ /** for tooltip: margin for search area for finding a domain object */
+ private static final int MARGIN_IN_PIXELS = 5;
+
+ /** the graph index; there is one graph per editor page (with identical content)*/
+ //private int graphIndex;
+ protected PecCommonTrace trace;
+ private String graphTitle;
+ private String helpContextId;
+
+ /** number of series to draw; each in a separate graph section */
+ private int typeAmount = 0;
+ protected List<Integer> drawSeries;
+
+ private int[][] origYCoords;
+ private int[][] points;
+
+ private int[] exact_mins;
+ private int[] exact_maxs;
+
+ private int[] mins;
+ private int[] maxs;
+
+ private double currentlyCalcFinalScale = -1;
+ private int currentlyCalcFinalHeight = -1;
+
+ protected FigureCanvas leftFigureCanvas;
+
+ /** marks graph as dirty so it gets re-drawn */
+ protected boolean dirty;
+
+ private PecCommonLegend legend;
+
+ /** GUI manager has knowledge off all IpcTraceGraphs; can broadcast some of the events */
+ private PecCommonGuiManager guiManager;
+
+ /**
+ * Constructor
+ * @param graphIndex the index of the graph (corresponds to the page in the editor)
+ * @param pecTrace the trace class
+ * @param uid the uid to identify the current editor
+ * @param guiManager IpcGuiManager which manages all graphs
+ * @param title The title of the graph
+ * @param helpContextIdMainPage
+ */
+ public PecCommonTraceGraph(int graphIndex, PecCommonTrace pecTrace, int uid, PecCommonGuiManager guiManager, String title, String helpContextIdMainPage) {
+ super(pecTrace);
+
+// this.graphIndex = graphIndex;
+ this.trace = pecTrace;
+ this.guiManager = guiManager;
+ this.graphTitle = title;
+ this.helpContextId = helpContextIdMainPage;
+
+ typeAmount = pecTrace.getValueTypes().length;
+
+ int sampleCount = pecTrace.getSampleAmount();
+ origYCoords = new int[typeAmount][sampleCount];
+ points = new int[typeAmount][sampleCount*2];
+ exact_mins = new int[typeAmount];
+ exact_maxs = new int[typeAmount];
+ mins = new int[typeAmount];
+ maxs = new int[typeAmount];
+
+ drawSeries = new ArrayList<Integer>();
+ //by default, draw all graphs on screen
+ showAll();
+
+ this.initialiseData();
+
+ ProfileVisualiser pV = NpiInstanceRepository.getInstance().getProfilePage(uid, graphIndex);
+ legend = createLegend(pV.getBottomComposite());
+ }
+
+ /**
+ * Creates the legend
+ * @param bottomComposite The composite to create the legend in
+ */
+ protected PecCommonLegend createLegend(Composite bottomComposite) {
+ return new PecCommonLegend(this, bottomComposite, getTitle(), trace);
+ }
+
+ /**
+ * Fill mins, maxs, and origYCoords arrays
+ */
+ private void initialiseData() {
+ Vector<GenericSample> sv = trace.samples;
+
+ Arrays.fill(exact_mins, Integer.MAX_VALUE);
+ Arrays.fill(exact_maxs, Integer.MIN_VALUE);
+
+ for (int x = 0; x < sv.size(); x++) {
+ PecCommonSample s = (PecCommonSample) sv.get(x);
+
+ for (int i = 0; i < typeAmount; i++) {
+ int value = s.values[i];
+
+ if (value < exact_mins[i]){
+ exact_mins[i] = value;
+ }
+ if (value > exact_maxs[i]){
+ exact_maxs[i] = value;
+ }
+
+ origYCoords[i][x] = value;
+ }
+ }
+
+ //let the graph draw in area [0, prettyMaxValue]
+ for (int i = 0; i < typeAmount; i++) {
+ mins[i] = 0;
+ maxs[i] = prettyMaxValue(exact_maxs[i]);
+ }
+ }
+
+ private void showAll(){
+ drawSeries.clear();
+ for (int i = 0; i < typeAmount; i++) {
+ drawSeries.add(i);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph#repaint()
+ */
+ @Override
+ public void repaint() {
+ this.parentComponent.repaintComponent();
+ }
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph#paint(org.eclipse.draw2d.Panel, org.eclipse.draw2d.Graphics)
+ */
+ @Override
+ public void paint(org.eclipse.draw2d.Panel panel,
+ org.eclipse.draw2d.Graphics graphics) {
+ this.setSize(this.getSize().width, getVisualSize().height);
+ this.drawDottedLineBackground(graphics, X_LEGEND_HEIGHT);
+
+ drawGraphs(panel, graphics);
+ drawLabelsInGraph(panel, graphics);
+
+ this.drawSelectionSection(graphics, X_LEGEND_HEIGHT);
+ }
+
+ private void drawLabelsInGraph(Panel panel, Graphics graphics) {
+
+ float sectionHeight = getSectionHeight();
+
+ if (sectionHeight > 60f){ //only draw the label if the section has a decent height
+
+ int edgeX = ((Viewport) panel.getParent()).getViewLocation().x;
+
+ for (int i = 0; i < drawSeries.size(); i++) {
+ int seriesIdx = drawSeries.get(i);
+ int y = (int) sectionHeight * i;
+
+ graphics.setForegroundColor(ColorConstants.black);
+ graphics.drawString(trace.getValueTypes()[seriesIdx],
+ edgeX + 10, y + 6);
+ if (sectionHeight > 80f){
+ graphics.drawString(String.format(Messages.PecCommonTraceGraph_0,
+ this.exact_mins[seriesIdx], this.exact_maxs[seriesIdx]),
+ edgeX + 10, y + 18);
+ }
+ }
+ }
+ }
+
+ private void drawGraphs(Panel panel, Graphics graphics) {
+ int visY = this.getVisualSize().height;
+ double scale = getScale();
+
+ if (dirty || this.currentlyCalcFinalHeight != visY
+ || this.currentlyCalcFinalScale != scale) {
+
+ Vector<GenericSample> sv = trace.samples;
+ float sectionHeight = getSectionHeight();
+
+ for (int sampleIdx = 0; sampleIdx < sv.size(); sampleIdx++) {
+ // calculate x-coordinate per sample
+ int xCoord = (int) (sampleIdx / scale);
+
+ for (int seriesIdx : drawSeries) {
+ points[seriesIdx][sampleIdx * 2] = xCoord;
+ points[seriesIdx][sampleIdx * 2 + 1] = convertValueToYCoordinate(origYCoords[seriesIdx][sampleIdx], mins[seriesIdx], maxs[seriesIdx], drawSeries.indexOf(seriesIdx), sectionHeight);
+ }
+ }
+
+ dirty = false;
+ this.currentlyCalcFinalHeight = visY;
+ this.currentlyCalcFinalScale = scale;
+ }
+
+ graphics.setForegroundColor(ColorConstants.blue);
+
+ for (int seriesIdx : drawSeries) {
+ graphics.drawPolyline(points[seriesIdx]);
+ }
+ }
+
+ /**
+ * Converts a y coordinate into the actual value.
+ * @param yCoordinate the y coordinate to convert
+ * @param minValue the lowest value in the series
+ * @param maxValue the highest value in the series
+ * @param section the number of the section (for 3 sections this would be 0, 1, or 2)
+ * @param sectionHeight the height of the section in pixels. All sections are of equal height
+ * @return the converted value
+ */
+ static int convertYCoordinateToValue(int yCoordinate, int minValue, int maxValue, int section, float sectionHeight){
+ float offset = sectionHeight * section;
+
+ float location = sectionHeight - (yCoordinate - offset) ;
+ return (int)(location * (maxValue - minValue) / sectionHeight ) + minValue;
+ }
+
+ /**
+ * Converts a value into a y coordinate; the opposite of convertYCoordinateToValue()
+ * @param value the value to convert
+ * @param minValue the lowest value in the series
+ * @param maxValue the highest value in the series
+ * @param section the number of the section (for 3 sections this would be 0, 1, or 2)
+ * @param sectionHeight the height of the section in points. All sections are of equal height
+ * @return the converted value
+ */
+ static int convertValueToYCoordinate(int value, int minValue, int maxValue, int section, float sectionHeight){
+ float offset = sectionHeight * section;
+
+ float location = (value - minValue) * sectionHeight / (maxValue - minValue); //lowest shown value is min (not 0)
+ return(int) ((sectionHeight - location) + offset);
+ }
+
+ /**
+ * Returns the height of a section in pixels. All sections are of equal height.
+ * @return
+ */
+ private float getSectionHeight(){
+ int visualHeight = getVisualSize().height - X_LEGEND_HEIGHT;
+ return drawSeries.size() == 0 ? visualHeight : visualHeight / drawSeries.size();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph#paintLeftLegend
+ * (org.eclipse.draw2d.FigureCanvas, org.eclipse.swt.graphics.GC)
+ */
+ @Override
+ public void paintLeftLegend(FigureCanvas figureCanvas, GC gc) {
+ GC localGC = gc;
+
+ if (gc == null)
+ gc = new GC(PIPageEditor.currentPageEditor().getSite().getShell());
+
+ Rectangle rect = ((GraphComposite) figureCanvas.getParent()).figureCanvas
+ .getClientArea();
+
+ int visY = rect.height;
+
+ float visYfloat = visY - X_LEGEND_HEIGHT;
+
+ if (visYfloat < 0f)
+ visYfloat = 0f;
+
+ gc.setForeground(ColorPalette.getColor(new RGB(100, 100, 100)));
+ gc.setBackground(ColorPalette.getColor(new RGB(255, 255, 255)));
+
+ int legendsToDraw = drawSeries.size();
+ float legendHeight = visYfloat / legendsToDraw;
+ double yIncrement = legendHeight / 10;
+ int previousBottom = 0;
+
+ for (int section = 0; section < legendsToDraw; section++) {
+ int seriesIdx = drawSeries.get(section);
+ int maxValue = maxs[seriesIdx];
+
+ float valuePerPixel = maxValue / legendHeight;
+
+ for (int k = 10; k >= 0; k--) {
+ // location for the value indicator is k * 1/10 the height of
+ // the
+ // height of the section
+ int y = (int) (legendHeight * (section+1) - (yIncrement * k));
+ int value = (int) ((((legendHeight * valuePerPixel) / 10.0) * k));
+
+ // construct the text for each scale
+ //use grouping for small numbers, then without grouping to fit the text
+ String sValue = String.format(value < 1000000 ? Messages.PecCommonTraceGraph_1 : Messages.PecCommonTraceGraph_2, value);
+
+ Point extent = gc.stringExtent(sValue);
+
+ gc.drawLine(IGenericTraceGraph.Y_LEGEND_WIDTH - 3, y + 1,
+ IGenericTraceGraph.Y_LEGEND_WIDTH, y + 1);
+
+ if (y >= previousBottom) {
+ gc.drawString(sValue, IGenericTraceGraph.Y_LEGEND_WIDTH
+ - extent.x - 4, y);
+ previousBottom = y + extent.y;
+ }
+ }
+ }
+
+ if (localGC == null) {
+ gc.dispose();
+ figureCanvas.redraw();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph#getTitle()
+ */
+ @Override
+ public String getTitle() {
+ return this.graphTitle;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.MouseMotionListener#mouseDragged(org.eclipse.draw2d.MouseEvent)
+ */
+ public void mouseDragged(MouseEvent arg0) {
+ // no-op
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.MouseMotionListener#mouseEntered(org.eclipse.draw2d.MouseEvent)
+ */
+ public void mouseEntered(MouseEvent arg0) {
+ // no-op
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.MouseMotionListener#mouseExited(org.eclipse.draw2d.MouseEvent)
+ */
+ public void mouseExited(MouseEvent arg0) {
+ // no-op
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.MouseMotionListener#mouseHover(org.eclipse.draw2d.MouseEvent)
+ */
+ public void mouseHover(MouseEvent arg0) {
+ // no-op
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.MouseMotionListener#mouseMoved(org.eclipse.draw2d.MouseEvent)
+ */
+ public void mouseMoved(MouseEvent me) {
+ //setting the tooltip
+ float sectionHeight = getSectionHeight();
+ int section = (int) (me.y / sectionHeight);
+ if (section < 0 || section >= drawSeries.size()){
+ this.setToolTipText(null);
+ return;
+ }
+ int seriesIdx = drawSeries.get(section);
+
+ PecCommonSample sample = getSampleUnderMouse(me);
+
+ if (sample != null){
+ setToolTipText(String.format(Messages.PecCommonTraceGraph_3, trace.getValueTypes()[seriesIdx], sample.sampleSynchTime /1000f, sample.values[seriesIdx]));
+ } else {
+ String tooltip = null;
+
+ //display default values
+
+ if (section < drawSeries.size()) {
+ double time = me.x * this.getScale();
+ if (time >= 0 && time <= trace.getLastSampleTime()){
+ int value = convertYCoordinateToValue(me.y, mins[seriesIdx], maxs[seriesIdx], section, sectionHeight);
+ tooltip = String.format(Messages.PecCommonTraceGraph_4, time / 1000f, value);
+ }
+ }
+
+ this.setToolTipText(tooltip);
+ }
+ }
+
+ /**
+ * Returns the sample for the given mouse location, or null
+ * @param me mouse event for the mouse location
+ * @return the sample if found, or null
+ */
+ private PecCommonSample getSampleUnderMouse(MouseEvent me) {
+ PecCommonSample ret = null;
+
+ if (drawSeries.size() == 0){
+ return ret; //no graphs are being drawn so no samples can be found
+ }
+
+ int mex = me.x;
+ int mey = me.y;
+ float sectionHeight = getSectionHeight();
+ int section = (int) (mey / sectionHeight);
+
+ if (section >= 0 && section < drawSeries.size()) {
+ int seriesIdx = drawSeries.get(section);
+ double idealTime = mex * this.getScale();
+ if (idealTime < 0 || idealTime > trace.getLastSampleTime()){
+ return null;
+ }
+ int idealValue = convertYCoordinateToValue(mey, mins[seriesIdx], maxs[seriesIdx], section, sectionHeight);
+
+ //calculate the boundary of the area in which to look for a sample
+ double leftBoudaryTime = ((mex - MARGIN_IN_PIXELS) * getScale());
+ if (leftBoudaryTime < 0){
+ leftBoudaryTime = 0;
+ }
+ double rightBoundaryTime = ((mex + MARGIN_IN_PIXELS) * getScale());
+ if (rightBoundaryTime < 0){
+ return null;
+ } else if (rightBoundaryTime > trace.getLastSampleTime()){
+ rightBoundaryTime = trace.getLastSampleTime();
+ }
+
+ int topBoundaryValue = convertYCoordinateToValue(Math.max((int)(section * sectionHeight), mey - MARGIN_IN_PIXELS), mins[seriesIdx], maxs[seriesIdx], section, sectionHeight);
+ int bottomBoundaryValue = convertYCoordinateToValue(Math.min((int)((section +1) * sectionHeight), mey + MARGIN_IN_PIXELS), mins[seriesIdx], maxs[seriesIdx], section, sectionHeight);
+
+ int leftSample = (int)(leftBoudaryTime / trace.getSamplingInterval());
+ int rightSample = (int)(rightBoundaryTime+.5 / trace.getSamplingInterval());
+
+ for (int i = leftSample; i <= rightSample; i++) {
+ PecCommonSample sample = (PecCommonSample)trace.getSample(i);
+ if (sample.values[seriesIdx] < topBoundaryValue && sample.values[seriesIdx] > bottomBoundaryValue){
+ //System.out.println("Cur: "+ sample.values[seriesIdx]+" Range: "+topBoundaryValue+", "+bottomBoundaryValue); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ //check whether we have found a closer match to mid-point
+ if (ret == null
+ || (Math.abs(sample.sampleSynchTime - idealTime) < Math.abs(ret.sampleSynchTime - idealTime))
+ || (ret.sampleSynchTime == sample.sampleSynchTime
+ && Math.abs(sample.values[seriesIdx] - idealValue) < Math.abs(ret.values[seriesIdx] - idealValue))){
+ ret = sample;
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Converts the passed X-coordiate into a time value (in milliseconds) using
+ * the scale provided. Makes sure the return value is non-negative.
+ *
+ * @param x
+ * the x coordinate to use
+ * @param scale
+ * the scale to use
+ * @return time in milliseconds
+ */
+ protected static double getTimeForXCoordinate(int x, double scale) {
+ double time = x * scale;
+ // mouse event may return out of range X, that may
+ // crash when we use it to index data array
+ time = time >= 0 ? time : 0;
+ return time;
+ }
+
+ /**
+ * Make the input value a pretty value to display, such as 423,345 => 500,000
+ * @param value the value to convert
+ * @return the prettier value
+ */
+ static int prettyMaxValue(final int value) {
+ double prettyVal = value;
+ int len = 0;
+
+ while (prettyVal >= 10) {
+ prettyVal/= 10;
+ len++;
+ }
+
+ prettyVal = Math.ceil(prettyVal);
+
+ if (prettyVal <= 1){
+ prettyVal = 1;
+ } else if (prettyVal <= 2){
+ prettyVal = 2;
+ } else if (prettyVal <= 3){
+ prettyVal = 3;
+ } else if (prettyVal <= 5){
+ prettyVal = 5;
+ } else {
+ prettyVal = 10;
+ }
+
+ return (int)(prettyVal * Math.pow(10, len));
+ }
+
+ /**
+ * Callback for PIEvent.SELECTION_AREA_CHANGED
+ * @param newStart new selection start
+ * @param newEnd new selection end
+ */
+ void selectionAreaChanged(double newStart, double newEnd) {
+ this.setSelectionStart(newStart);
+ this.setSelectionEnd(newEnd);
+
+ trace.selectionAreaChanged(newStart, newEnd);
+ legend.refreshLegend();
+
+ this.repaint();
+ }
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.visual.PIEventListener#piEventReceived(com.nokia.carbide.cpp.internal.pi.visual.PIEvent)
+ */
+ public void piEventReceived(PIEvent be) {
+ switch (be.getType()) {
+
+ case PIEvent.SELECTION_AREA_CHANGED:
+ double[] values = (double[]) be.getValueObject();
+ //broadcast to all IPC graphs
+ guiManager.selectionAreaChanged(values[0], values[1]);
+ break;
+
+ case PIEvent.SCROLLED:
+ Event event = ((Event) be.getValueObject());
+ //this broadcasts to all graphs on this PICompositePanel
+ this.parentComponent.setScrolledOrigin(event.x, event.y, (FigureCanvas)event.data);
+ this.repaint();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph#graphMaximized(boolean)
+ */
+ @Override
+ public void graphMaximized(boolean value) {
+ legend.setLegendMaximised(value);
+ }
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph#graphVisibilityChanged(boolean)
+ */
+ @Override
+ public void graphVisibilityChanged(boolean value) {
+ legend.setLegendVisible(value);
+ }
+
+ @Override
+ public void action(String action) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * Adds or removes a series to be drawn
+ * @param seriesId series id of the series to add or remove
+ * @param add true if series is to be added, false for remove
+ */
+ public void addOrRemoveSeries(int seriesId, boolean add){
+ if (add){
+ if (!drawSeries.contains(seriesId)){
+ drawSeries.add(seriesId);
+ Collections.sort(drawSeries);
+ }
+ } else {
+ drawSeries.remove(Integer.valueOf(seriesId));
+ }
+
+ //repaint graphs
+ redrawGraphArea();
+ }
+
+ private void redrawGraphArea() {
+ dirty = true;
+ setGraphImageChanged(true);
+ repaint();
+ }
+
+ /**
+ * Removes all series from display
+ */
+ public void removeAllSeries(){
+ drawSeries.clear();
+ redrawGraphArea();
+
+ }
+
+ /**
+ * Shows all series in graph area
+ */
+ public void showAllSeries(){
+ showAll();
+ redrawGraphArea();
+ }
+
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.plugin.model.ITitleBarMenu#addTitleBarMenuItems()
+ */
+ public Action[] addTitleBarMenuItems() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.nokia.carbide.cpp.internal.pi.plugin.model.ITitleBarMenu#getContextHelpId()
+ */
+ public String getContextHelpId() {
+ return this.helpContextId;
+ }
+
+}