ExtDerivedMetricDlg.java

Go to the documentation of this file.
00001 
00004 package edu.rice.cs.hpc.viewer.metric;
00005 // jface
00006 import java.util.Formatter;
00007 import java.util.FormatterClosedException;
00008 import java.util.IllegalFormatException;
00009 
00010 import org.eclipse.jface.dialogs.TitleAreaDialog;
00011 import org.eclipse.jface.dialogs.MessageDialog;
00012 import org.eclipse.jface.layout.GridDataFactory;
00013 import org.eclipse.jface.layout.GridLayoutFactory;
00014 import org.eclipse.jface.layout.LayoutConstants;
00015 // swt
00016 import org.eclipse.swt.SWT;
00017 import org.eclipse.swt.graphics.Point;
00018 import org.eclipse.swt.widgets.Composite;
00019 import org.eclipse.swt.widgets.Control;
00020 import org.eclipse.swt.widgets.ExpandBar;
00021 import org.eclipse.swt.widgets.ExpandItem;
00022 import org.eclipse.swt.widgets.Group;
00023 import org.eclipse.swt.widgets.Shell;
00024 import org.eclipse.swt.widgets.Label;
00025 import org.eclipse.swt.widgets.Combo;
00026 import org.eclipse.swt.widgets.Button;
00027 import org.eclipse.swt.widgets.Text;
00028 import org.eclipse.swt.events.KeyAdapter;
00029 import org.eclipse.swt.events.KeyEvent;
00030 import org.eclipse.swt.events.MouseAdapter;
00031 import org.eclipse.swt.events.MouseEvent;
00032 import org.eclipse.swt.events.SelectionListener;
00033 import org.eclipse.swt.events.SelectionEvent;
00034 // hpcviewer
00035 import edu.rice.cs.hpc.common.util.UserInputHistory;
00036 import edu.rice.cs.hpc.data.experiment.Experiment;
00037 import edu.rice.cs.hpc.data.experiment.metric.*;
00038 import edu.rice.cs.hpc.data.experiment.metric.BaseMetric.AnnotationType;
00039 import edu.rice.cs.hpc.data.experiment.scope.Scope;
00040 // math expression
00041 import com.graphbuilder.math.*;
00042 import com.graphbuilder.math.func.*;
00043 
00048 public class ExtDerivedMetricDlg extends TitleAreaDialog {
00049     
00050     static public enum MetricDisplayFormat {
00051         Default, Percent, Custom
00052     }
00053     
00054     //------------- Constants
00055     
00056     final private String FORMAT_PERCENT = "%.2f %%";
00057     
00058     //------------- GUI variables
00059     private Combo cbName;
00060     private Combo cbExpression;
00061     private Button btnPercent;
00062     private Text txtFormat;
00063     private Button btnCustomFormat;
00064     private Button btnPercentFormat;
00065     private Button btnDefaultFormat;
00066     
00067     // ------------ Metric and math variables
00068     private String []arrStrMetrics;
00069     private Expression expFormula;
00070     private final ExtFuncMap fctMap;
00071     private final MetricVarMap varMap;
00072     private DerivedMetric metric;
00073     
00074     // ------------- Others
00075     static private final String HISTORY_FORMULA = "formula";            //$NON-NLS-1$
00076     static private final String HISTORY_METRIC_NAME = "metric_name";    //$NON-NLS-1$
00077     private Experiment experiment;
00078     private Point expression_position;
00079     
00080     // ------------- object for storing history of formula and metric names
00081     private UserInputHistory objHistoryFormula;
00082     private UserInputHistory objHistoryName;
00083 
00084     //==========================================================
00085       // ---- Constructor
00086       //==========================================================
00092     public ExtDerivedMetricDlg(Shell parentShell, Experiment exp) {
00093         this(parentShell, exp, null);
00094     }
00095     
00096     public ExtDerivedMetricDlg(Shell parent, Experiment exp, Scope s) {
00097         super(parent);
00098         experiment = exp;
00099         this.setMetrics(exp.getMetrics());
00100         this.fctMap = new ExtFuncMap(exp.getMetrics());
00101         this.varMap = new MetricVarMap ( s, exp );
00102     }
00103     
00104       //==========================================================
00105       // ---- GUI CREATION
00106       //==========================================================
00107 
00114       protected Control createContents(Composite parent) {
00115         Control contents = super.createContents(parent);
00116 
00117         // Set the title
00118         setTitle("Creating a derived metric");
00119 
00120         // Set the message
00121         setMessage("A derived metric is a spreadsheet-like formula using other metrics (variables), operators, functions,\n"
00122                     + "and numerical constants.\n");
00123 
00124         return contents;
00125       }
00126 
00127       /*
00128        * {@docRoot org.eclipse.jface.dialogs.TitleAreaDialog}
00129        * @see {@link org.eclipse.jface.dialogs.TitleAreaDialog} 
00130        */
00131       protected Control createDialogArea(Composite parent) {
00132         Composite composite = (Composite) super.createDialogArea(parent);
00133         Group grpBase = new Group(composite, SWT.NONE);
00134         grpBase.setText("Derived metric definition");
00135         
00136         Point ptMargin = LayoutConstants.getMargins(); 
00137         ptMargin.x = 5;
00138         ptMargin.y = 5;
00139 
00140         Composite expressionArea = new Composite(grpBase, SWT.NONE);
00141         {
00142             Group grpExpression = new Group(expressionArea, SWT.NONE);
00143             
00144             //--------------------------------------------
00145             // name of the metric
00146             //--------------------------------------------
00147             final Composite nameArea = new Composite(grpExpression, SWT.NONE);
00148             final Label lblName = new Label(nameArea, SWT.LEFT);
00149             lblName.setText("Name:");
00150             
00151             this.cbName = new Combo(nameArea, SWT.NONE);
00152             this.cbName.setToolTipText("Name of the derived metric");
00153             objHistoryName = new UserInputHistory( ExtDerivedMetricDlg.HISTORY_METRIC_NAME );
00154             this.cbName.setItems( this.objHistoryName.getHistory() );
00155             
00156             if (metric != null) {
00157                 cbName.setText(metric.getDisplayName());
00158             }
00159             
00160             //--------------------------------------------
00161             // formula
00162             //--------------------------------------------          
00163             Label lblFormula = new Label(nameArea, SWT.NONE);
00164             lblFormula.setText("Formula: ");
00165             
00166             this.cbExpression = new Combo(nameArea, SWT.NONE);
00167             objHistoryFormula = new UserInputHistory(HISTORY_FORMULA);
00168             this.cbExpression.setItems( objHistoryFormula.getHistory() );
00169             cbExpression.setToolTipText("A spreadsheet-like formula using other metrics (variables), arithmetic operators, functions, and numerical constants");
00170 
00171             if (metric != null) {
00172                 cbExpression.setText( metric.getFormula().toString() );
00173             }
00174             
00175             GridLayoutFactory.fillDefaults().numColumns(2).generateLayout(nameArea);
00176 
00177             Label lbl = new Label(grpExpression, SWT.WRAP);
00178             lbl.setText("There are two kinds of metric variables: point-wise and aggregate.  The former is like a spreadsheet cell, the "
00179                         + "latter is like a spreadsheet-column sum.  To form a variable, prepend '$' and '@', respectively, to a metric id.  "
00180                         + "For instance, the formula\n"
00181                         + "    (($2 - $1) * 100.0) / @1\n"
00182                         + "divides the scaled difference of the point-wise metrics 2 and 1 by the aggregate value of metric 1.");
00183             
00184             expression_position = new Point(0,0);
00185             cbExpression.addKeyListener( new KeyAdapter(){
00186 
00187                 public void keyReleased(KeyEvent e) {
00188                     expression_position = cbExpression.getSelection();
00189                 }
00190                 
00191             });
00192             
00193             cbExpression.addMouseListener( new MouseAdapter(){
00194                 public void mouseUp(MouseEvent e)  {
00195                     if (cbExpression.getClientArea().contains(e.x, e.y)) {
00196                         expression_position = cbExpression.getSelection();
00197                     }
00198                 }
00199                 
00200             });
00201             
00202             GridLayoutFactory.fillDefaults().numColumns(1).generateLayout(grpExpression);
00203             
00204             //--------------- inserting metric
00205             Group grpInsertion = new Group(expressionArea, SWT.NONE);
00206             grpInsertion.setText("Assistance:");
00207 
00208             Label lblMetric = new Label(grpInsertion, SWT.NONE);
00209             lblMetric.setText("Metrics:");
00210             
00211             // combo box that lists the metrics
00212             final Combo cbMetric = new Combo(grpInsertion, SWT.READ_ONLY);
00213             cbMetric.setItems(this.arrStrMetrics);
00214             cbMetric.setText(this.arrStrMetrics[0]);
00215 
00216             //---------------------------------------------------------------
00217             // button to insert the metric code into the expression field
00218             //---------------------------------------------------------------
00219 
00220             final Composite buttonArea = new Composite(grpInsertion, SWT.NONE);
00221             final Button btnMetric = new Button(buttonArea, SWT.PUSH);
00222             btnMetric.setText("Point-wise");
00223             btnMetric.setToolTipText("Insert the metric as point-wise variable in the formula by prepending with '$' sign");
00224             
00225             btnMetric.addSelectionListener(new SelectionListener() {
00226                 public void widgetSelected(SelectionEvent e) {
00227                     insertMetricToFormula("$", cbMetric.getSelectionIndex());
00228                 }
00229                 public void widgetDefaultSelected(SelectionEvent e) {
00230                     
00231                 }
00232             });
00233             
00234             final Button btnAggregate = new Button(buttonArea, SWT.PUSH);
00235             btnAggregate.setText("Aggregate");
00236             btnAggregate.setToolTipText("Insert the metric as aggregate variable in the formula by prepending with '@' sign");
00237             
00238             btnAggregate.addSelectionListener(new SelectionListener() {
00239                 public void widgetSelected(SelectionEvent e) {
00240                     insertMetricToFormula("@", cbMetric.getSelectionIndex());
00241                 }
00242                 public void widgetDefaultSelected(SelectionEvent e) {
00243                     
00244                 }
00245             });
00246             GridLayoutFactory.fillDefaults().numColumns(2).generateLayout(buttonArea);
00247 
00248             //---------------- inserting function
00249             Label lblFunc = new Label(grpInsertion, SWT.NONE);
00250             lblFunc.setText("Functions:");
00251             
00252             final Combo cbFunc = new Combo(grpInsertion, SWT.READ_ONLY);
00253             fctMap.loadDefaultFunctions();
00254             Function arrFct[] = fctMap.getFunctions();
00255             
00256             // create the list of the name of the function
00257             // list of the name  of the function and its arguments
00258             final String []arrFunctions = new String[arrFct.length];
00259             // the list of the name of the function to be inserted
00260             final String []arrFuncNames = fctMap.getFunctionNames();
00261             for(int i=0;i<arrFct.length;i++) {
00262                 arrFunctions[i] = arrFct[i].toString();
00263             }
00264             // insert the name of the function into the combo box
00265             if(arrFunctions != null && arrFunctions.length>0) {
00266                 cbFunc.setItems(arrFunctions);
00267                 // by default insert the toplist function
00268                 cbFunc.setText(arrFunctions[0]);
00269             }
00270             
00271             final Button btnFunc = new Button(grpInsertion, SWT.PUSH);
00272             btnFunc.setText("Insert function");
00273             btnFunc.addSelectionListener(new SelectionListener() {
00274                  // action to insert the name of the function into the formula text
00275                 public void widgetSelected(SelectionEvent e) {
00276                     Point p = expression_position;
00277                     String sFunc = arrFuncNames[cbFunc.getSelectionIndex()];
00278                     StringBuffer sb = new StringBuffer( cbExpression.getText() );
00279                     int iLen = sFunc.length();
00280                     sb.insert( p.x, sFunc );
00281                     sb.insert( p.x + iLen, "()" );
00282                     p.x = p.x + iLen + 1;
00283                     p.y = p.x;
00284                     cbExpression.setText( sb.toString() );
00285                     cbExpression.setSelection( p );
00286                 }
00287                 public void widgetDefaultSelected(SelectionEvent e) {
00288                     
00289                 }
00290             });
00291             
00292             final Label lblOperators = new Label(grpInsertion, SWT.NONE);
00293             lblOperators.setText("Operators: ");
00294             final Label lblOpValues  = new Label(grpInsertion, SWT.NONE);
00295             lblOpValues.setText("( ) + - * / ^");
00296 
00297 
00298             // do not expand the group
00299             GridDataFactory.fillDefaults().grab(false, false).applyTo(grpInsertion);
00300             GridLayoutFactory.fillDefaults().numColumns(3).generateLayout(grpInsertion);
00301             
00302             GridLayoutFactory.fillDefaults().numColumns(1).generateLayout(expressionArea);
00303         }
00304         GridLayoutFactory.fillDefaults().margins(ptMargin).generateLayout(grpBase);
00305         
00306         //-------
00307         // options
00308         //-------
00309         ExpandBar barOptions = new ExpandBar(composite,SWT.V_SCROLL);
00310         {
00311             Composite cOptions = new Composite (barOptions, SWT.NONE);
00312             GridLayoutFactory.fillDefaults().numColumns(1).margins(ptMargin).generateLayout(cOptions);
00313             
00314             // percent option
00315             this.btnPercent = new Button(cOptions, SWT.CHECK);
00316             this.btnPercent.setText("Augment metric value display with a percentage relative to column total");
00317             this.btnPercent.setToolTipText("For each metric value, display the annotation of percentage relative to aggregate metric value");
00318             
00319             // format option
00320             //final Composite cFormat = new Composite( cOptions, SWT.NONE );
00321             final Composite cCustomFormat = new Composite( cOptions, SWT.NONE );
00322             
00323             btnDefaultFormat = new Button(cCustomFormat, SWT.RADIO);
00324             btnDefaultFormat.setText("Default format");
00325             new Label( cCustomFormat, SWT.NONE );
00326             
00327             btnPercentFormat = new Button(cCustomFormat, SWT.RADIO);
00328             btnPercentFormat.setText("Display metric value as percent");
00329             new Label( cCustomFormat, SWT.NONE );
00330             
00331             btnCustomFormat = new Button(cCustomFormat, SWT.RADIO);
00332             btnCustomFormat.setText("Custom format");
00333             
00334             txtFormat = new Text(cCustomFormat, SWT.BORDER);
00335             final String txtCustomFormat = "The format is based on java.util.Formatter class which is almost equivalent to C's printf format. "; 
00336             txtFormat.setToolTipText(txtCustomFormat);
00337             btnCustomFormat.addSelectionListener(new SelectionListener(){
00338 
00339                 public void widgetSelected(SelectionEvent e) {
00340                     txtFormat.setEnabled(true); 
00341                 }
00342 
00343                 public void widgetDefaultSelected(SelectionEvent e) {}  
00344             });
00345             
00346             btnPercentFormat.addSelectionListener(new SelectionListener(){
00347 
00348                 public void widgetSelected(SelectionEvent e) {
00349                     txtFormat.setEnabled(false);
00350                     txtFormat.setFocus();
00351                 }
00352 
00353                 public void widgetDefaultSelected(SelectionEvent e) {}
00354                 
00355             });
00356             btnDefaultFormat.setSelection(true);
00357 
00358             if (metric != null) {
00359                 boolean bPercent = metric.getAnnotationType() == BaseMetric.AnnotationType.PERCENT;
00360                 btnPercent.setSelection( bPercent );
00361                 
00362                 IMetricValueFormat format = metric.getDisplayFormat();
00363                 if (format instanceof MetricValuePredefinedFormat) {
00364                     String strFormat = ((MetricValuePredefinedFormat)format ).getFormat();
00365                     txtFormat.setText( strFormat  );
00366                     
00367                     if (strFormat.equals( FORMAT_PERCENT )) {
00368                         btnPercentFormat.setSelection(true);
00369                         
00370                         // only one option is allowed. If one is enabled, the other is disabled
00371                         btnDefaultFormat.setSelection(false);
00372                     }
00373                 }
00374             }
00375             
00376             // make sure to initialize the state of the text
00377             txtFormat.setEnabled(false); 
00378             
00379             final Label lblCustomFormat = new Label( cOptions, SWT.NONE );
00380             lblCustomFormat.setText(txtCustomFormat + "\n"
00381                     + "Example: '%6.2f ' will display 6 digit floating-points with 2 digit precision. ");
00382             
00383             GridLayoutFactory.fillDefaults().numColumns(2).generateLayout(cCustomFormat);
00384             
00385             //GridLayoutFactory.fillDefaults().numColumns(1).generateLayout(cFormat);
00386             
00387             // item for expansion bar
00388             ExpandItem eiOptions = new ExpandItem(barOptions, SWT.NONE, 0);
00389             eiOptions.setText("Advanced options");
00390             eiOptions.setHeight(cOptions.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
00391             eiOptions.setControl(cOptions);
00392             eiOptions.setExpanded(true);
00393             
00394             barOptions.setToolTipText("Optional settings");
00395         }
00396 
00397         GridLayoutFactory.fillDefaults().numColumns(1).margins(ptMargin).generateLayout(
00398                 composite);
00399         return composite;
00400       }
00401       
00402       
00403       /*****
00404        * insert the selected metric from the combo box into the formula field
00405        * 
00406        * @param signToPrepend: either '$' or '@'
00407        * @param selection_index
00408        */
00409       private void insertMetricToFormula(String signToPrepend, int selection_index) {
00410           final String sText = cbExpression.getText();
00411           final int iSelIndex = expression_position.x; 
00412           StringBuffer sBuff = new StringBuffer(sText);
00413 
00414           // insert the metric variable ( i.e.: $ + metric index)
00415           final String sMetricIndex = signToPrepend + experiment.getMetric(selection_index).getShortName() ; 
00416           sBuff.insert(iSelIndex, sMetricIndex );
00417           cbExpression.setText(sBuff.toString());
00418 
00419           // put cursor after the metric variable
00420           Point p = new Point(iSelIndex + sMetricIndex.length(), iSelIndex + sMetricIndex.length());
00421           cbExpression.setSelection( p );
00422           expression_position = cbExpression.getSelection();
00423 
00424       }
00425 
00430       private boolean checkExpression() {
00431           boolean bResult = false;
00432             String sExpression = this.cbExpression.getText();
00433             if(sExpression.length() > 0) {
00434                 try {
00435                     expFormula = ExpressionTree.parse(sExpression);
00436                     bResult = evaluateExpression ( this.expFormula );
00437                 } catch (ExpressionParseException e) {
00438                     MessageDialog.openError(this.getShell(), "Invalid expression", e.getDescription());
00439                 }
00440             } else {
00441                 MessageDialog.openError(this.getShell(), "Error: empty expression", 
00442                     "An expression can not be empty.");
00443             }
00444           return bResult;
00445       }
00446       
00447       /***
00448        * verify the validity of custom format
00449        * @return
00450        */
00451       private boolean checkFormat() {
00452           boolean bResult = true;
00453           String sError = null;
00454           if (this.btnCustomFormat.getSelection()) {
00455               String sFormat = txtFormat.getText();
00456               
00457               // custom format if selected, cannot be null
00458               if (sFormat.length()==0) {
00459                   bResult = false;
00460                   sError = "Custom format cannot be empty.";
00461               }
00462               try {
00463                   Formatter format = new Formatter();
00464                   format.format(sFormat, 1.0);
00465                   
00466               } catch (IllegalFormatException e) {
00467                   sError = "Format is incorrect.";
00468                   bResult = false;
00469               } catch (FormatterClosedException e) {
00470                   bResult = false;
00471                   sError = "Illegal format.";
00472               }
00473           }
00474           if (!bResult)
00475               MessageDialog.openError(getShell(), "Format syntax error", sError);
00476           return bResult;
00477       }
00478       
00484       private boolean evaluateExpression ( Expression objExpression ) {
00485             try {
00486                 objExpression.eval( varMap, fctMap);
00487                 // if there is no exception, we assume everything goes fine
00488                 return true;
00489                 
00490             } catch(java.lang.Exception e) {
00491                 // should throw an exception
00492                 MessageDialog.openError( this.getShell(), "Error: incorrect expression", e.getMessage());
00493             }
00494 
00495             return false;
00496         }
00497       
00498       
00499       /*****
00500        * perform metric creation or update (depending whether the metric has been 
00501        *    created or not)
00502        * 
00503        * @return the new metric
00504        */
00505       private DerivedMetric doAction() {
00506 
00507           AnnotationType annType = AnnotationType.NONE;
00508           
00509           // -----------------------------
00510           // display the percentage ?
00511           // -----------------------------
00512           
00513           if (btnPercent.getSelection()) {
00514               annType = AnnotationType.PERCENT;
00515           }
00516 
00517           // -----------------------------
00518           // create or update a metric ?
00519           // -----------------------------
00520           
00521           if (metric == null) {
00522               
00523               // create a new metric
00524               
00525               int metricLastIndex   = experiment.getMetricCount() -1;
00526               BaseMetric metricLast = experiment.getMetric(metricLastIndex);
00527               
00528               String metricLastID = metricLast.getShortName();
00529               metricLastIndex = Integer.valueOf(metricLastID) + 1;
00530               metricLastID = String.valueOf(metricLastIndex);
00531 
00532               metric = new DerivedMetric(experiment, expFormula, 
00533                       cbName.getText(), metricLastID, experiment.getMetricCount(), 
00534                       annType, MetricType.INCLUSIVE);
00535               
00536           } else {
00537               // update the existing metric
00538               metric.setDisplayName( cbName.getText() );
00539               metric.setAnnotationType(annType);
00540               metric.setExpression(expFormula);
00541           }
00542           
00543           // -----------------------------
00544           // set the displayed format (case of custom format)
00545           // -----------------------------
00546 
00547           final String sFormat = txtFormat.getText();
00548           IMetricValueFormat objFormat;
00549           
00550           if ( btnCustomFormat.getSelection() && (sFormat != null) ) {
00551               
00552               // user has specified specific format. Let's set it to the metric
00553               objFormat = new MetricValuePredefinedFormat(sFormat);
00554               metric.setDisplayFormat(objFormat);
00555               
00556           } else if (btnPercentFormat.getSelection()) {
00557               objFormat = new MetricValuePredefinedFormat(FORMAT_PERCENT);
00558               metric.setDisplayFormat(objFormat);
00559           }
00560 
00561           return metric;
00562       }
00563 
00564       //==========================================================
00565       // ---- PUBLIC METHODS
00566       //==========================================================
00567         
00568       /****
00569        * setup the dialog with the list of metrics
00570        * 
00571        * @param listOfMetrics
00572        */
00573       public void setMetrics(BaseMetric []listOfMetrics) {
00574           int nbMetrics = listOfMetrics.length;
00575           this.arrStrMetrics = new String[nbMetrics];
00576           for(int i=0;i<nbMetrics;i++) {
00577               BaseMetric metric = listOfMetrics[i];
00578               // laksono 2009.12.15: we need to use the shortname instead of the index
00579               this.arrStrMetrics[i] = metric.getShortName() + ": "+ metric.getDisplayName();
00580           }
00581       }
00582       
00583       /***
00584        * set the default metric to modify
00585        * @param metric to modify
00586        */
00587       public void setMetric( DerivedMetric metric ) {
00588           this.metric = metric;
00589       }
00590 
00591       /******
00592        * return the new derived or updated metric
00593        * @return
00594        */
00595       public DerivedMetric getMetric() {
00596           return metric;
00597       }
00598       
00599       
00603       public void okPressed() {
00604         if(this.checkExpression() && this.checkFormat()) {
00605             // save the options for further usage (required by the caller)
00606             doAction();
00607             
00608             // save user history
00609             objHistoryFormula.addLine( cbExpression.getText() );
00610             objHistoryName.addLine( cbName.getText() );
00611 
00612             super.okPressed();
00613         }
00614       } 
00615 }

Generated on 5 May 2015 for HPCVIEWER by  doxygen 1.6.1