package net.certware.measurement.spm.view.handlers; import java.util.ArrayList; import java.util.List; import net.certware.core.ICertWareConstants; import net.certware.core.ui.dialog.ISelectionValidator; import net.certware.core.ui.dialog.ResourceSelectionDialog2; import net.certware.core.ui.log.CertWareLog; import net.certware.measurement.sco.ArtifactCommit; import net.certware.measurement.sco.CommitHistory; import net.certware.measurement.sco.CriticalDefectChangeOrders; import net.certware.measurement.sco.ImprovementChangeOrders; import net.certware.measurement.sco.NewFeatureChangeOrders; import net.certware.measurement.sco.NormalDefectChangeOrders; import net.certware.measurement.sco.TotalChangeOrders; import net.certware.measurement.smm.Accumulator; import net.certware.measurement.smm.Annotation; import net.certware.measurement.spm.BaselineCaseSizeMeasure; import net.certware.measurement.spm.CriticalAndNormalChangeOrderCount; import net.certware.measurement.spm.CriticalDefectChangeOrderCount; import net.certware.measurement.spm.EndProductQuality; import net.certware.measurement.spm.ImprovementChangeOrderCount; import net.certware.measurement.spm.InProgressIndicator; import net.certware.measurement.spm.NewFeatureChangeOrderCount; import net.certware.measurement.spm.NormalDefectChangeOrderCount; import net.certware.measurement.spm.ProjectCommit; import net.certware.measurement.spm.ProjectModel; import net.certware.measurement.spm.ProjectScope; import net.certware.measurement.spm.SpmFactory; import net.certware.measurement.spm.TotalCaseSizeMeasure; import net.certware.measurement.spm.TotalChangeOrderCount; import net.certware.measurement.spm.UsageTimeMeasure; import net.certware.measurement.spm.view.Activator; import net.certware.measurement.spm.view.preferences.PreferenceConstants; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.handlers.HandlerUtil; /** * Gathers change orders. * @author mrb * @since 2.0.0 */ public class GatherOrdersHandler extends AbstractHandler { /** * Handles the gather orders command request. * @param event used to provide context * @return always returns null * @throws ExecutionException if context fails * @see org.eclipse.core.commands.IHandler#execute(ExecutionEvent) */ @Override public Object execute(ExecutionEvent event) throws ExecutionException { try { // fetch workbench context IWorkbenchPart latestPart = HandlerUtil.getActivePartChecked(event); IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event); ISelectionService service = window.getSelectionService(); IStructuredSelection iss = (IStructuredSelection)service.getSelection(); // the selection is an SPM project model or project commit if ( iss == null ) { return null; } Object first = iss.getFirstElement(); // get the SCO resource by prompting the user // presumes the action is run in the UI thread // using the CertWare version of the JFace dialog so we can apply a validator filter ResourceSelectionDialog2 dialog = new ResourceSelectionDialog2(latestPart.getSite().getShell(), ResourcesPlugin.getWorkspace().getRoot(), "Select SCO file:"); dialog.setTitle("Change Order File Selection"); dialog.setBlockOnOpen(true); dialog.setHelpAvailable(true); dialog.setValidator(new ISelectionValidator() { @Override public boolean isValid(IResource resource) { if ( resource instanceof IProject ) { return true; } if ( resource instanceof IFolder ) { return true; } if ( resource instanceof IFile ) { IFile ifile = (IFile)resource; if ( ifile.getFileExtension().equals( ICertWareConstants.SCO_EXTENSION) ) { return true; } } return false; }}); if ( dialog.open() == Dialog.CANCEL ) { return null; } Object results[] = dialog.getResult(); List<CommitHistory> listOfHistories = new ArrayList<CommitHistory>(); // collect change order artifact lists from the selected files for ( Object o : results ) { if ( o instanceof IFile ) { IFile f = (IFile)o; if ( f.getFileExtension().equals( ICertWareConstants.SCO_EXTENSION ) == false ) { CertWareLog.logWarning(String.format("%s %s","Skipped processing change orders from invalid file",f.getName())); continue; } CommitHistory commitHistory = loadOrdersFromFile(f); if ( commitHistory != null ) { listOfHistories.add(commitHistory); CertWareLog.logInfo(String.format("%s %s","Processing change orders from file",f.getName())); } } } // find preference to determine whether to match commit IDs in the files // project commit if ( first instanceof ProjectCommit ) { ProjectCommit pc = (ProjectCommit)first; if ( computeOrders(pc,listOfHistories) == true ) { return null; } } // project model selection, do for each commit if ( first instanceof ProjectModel ) { // check raw statistics and compute metrics ProjectModel pm = (ProjectModel)first; for ( ProjectCommit pc : pm.getCommits() ) { if ( computeOrders(pc,listOfHistories) == true ) { // nothing logged per commit } } return null; } } catch (ExecutionException e) { CertWareLog.logError("Gathering orders", e); } return null; } /** * Gets a list of the annotations attached to a project commit element. * Builds the list with each annotation separated by a comma character. * @param pc project commit * @return string delimited by commas */ private String getName(ProjectCommit pc) { EList<Annotation> annotations = pc.getAnnotation(); StringBuffer sb = new StringBuffer(); boolean subsequentPass = false; for ( Annotation a : annotations ) { if ( subsequentPass ) { sb.append( ',' ); } sb.append( a.getText() ); subsequentPass = true; } return sb.toString(); } /** * Compute the metrics given a project commit. * Check whether the raw statistics are present. * Updates the model elements in the given commit. * @param pc project commit to check * @param histories list of history lists to process from change orders * @return true if orders were successfully gathered */ public boolean computeOrders(ProjectCommit pc, List<CommitHistory> histories) { CriticalDefectChangeOrderCount cdcoc = null; NormalDefectChangeOrderCount ndcoc = null; ImprovementChangeOrderCount icoc = null; NewFeatureChangeOrderCount nfcoc = null; TotalChangeOrderCount tcoc = null; CriticalAndNormalChangeOrderCount cncoc = null; BaselineCaseSizeMeasure bcs = null; TotalCaseSizeMeasure tcs = null; UsageTimeMeasure ut = null; EndProductQuality endProductQuality = null; InProgressIndicator inProgressIndicator = null; ProjectScope projectScope = null; // preference store fetch IPreferenceStore store = Activator.getDefault().getPreferenceStore(); boolean matchCommitIds = store.getBoolean( PreferenceConstants.P_MATCH_COMMITS ); boolean includeLineCounts = store.getBoolean( PreferenceConstants.P_INCLUDE_LINE_COUNTS ); boolean clearMeasurements = store.getBoolean(PreferenceConstants.P_CLEAR_MEASUREMENTS); // gather individual statistics by iterating over all commit elements TreeIterator<?> ti = pc.eAllContents(); while( ti.hasNext() ) { EObject eo = (EObject)ti.next(); // scopes if ( eo instanceof ProjectScope ) { projectScope = (ProjectScope)eo; continue; } // statistics if ( eo instanceof CriticalDefectChangeOrderCount ) { cdcoc = (CriticalDefectChangeOrderCount)eo; continue; } if ( eo instanceof NormalDefectChangeOrderCount ) { ndcoc = (NormalDefectChangeOrderCount)eo; continue; } if ( eo instanceof ImprovementChangeOrderCount ) { icoc = (ImprovementChangeOrderCount)eo; continue; } if ( eo instanceof NewFeatureChangeOrderCount ) { nfcoc = (NewFeatureChangeOrderCount)eo; continue; } if ( eo instanceof TotalChangeOrderCount ) { tcoc = (TotalChangeOrderCount)eo; continue; } if ( eo instanceof CriticalAndNormalChangeOrderCount ) { cncoc = (CriticalAndNormalChangeOrderCount)eo; continue; } if ( eo instanceof BaselineCaseSizeMeasure ) { bcs = (BaselineCaseSizeMeasure)eo; continue; } if ( eo instanceof TotalCaseSizeMeasure ) { tcs = (TotalCaseSizeMeasure)eo; continue; } if ( eo instanceof UsageTimeMeasure ) { ut = (UsageTimeMeasure)eo; continue; } // categories if ( eo instanceof EndProductQuality) { endProductQuality = (EndProductQuality)eo; continue; } if ( eo instanceof InProgressIndicator) { inProgressIndicator = (InProgressIndicator)eo; } } // for commit // check input statistics // creates a list of missing items, if any StringBuffer sb = new StringBuffer(); if ( projectScope == null ) { // try to find in parent model projectScope = findProjectScopeFromCommit(pc); if ( projectScope == null ) { sb.append("Project scope").append('\n'); } } // check whether categories are already present // if not, create them and add to the project commit if ( endProductQuality == null ) { endProductQuality = SpmFactory.eINSTANCE.createEndProductQuality(); endProductQuality.setName("End Product Quality"); pc.getModelElement().add(endProductQuality); CertWareLog.logInfo("Added end product quality characteristic to project commit"); } if ( inProgressIndicator == null ) { inProgressIndicator = SpmFactory.eINSTANCE.createInProgressIndicator(); inProgressIndicator.setName("In-Progress Indicator"); pc.getModelElement().add(inProgressIndicator); CertWareLog.logInfo("Added in-progress indicator characteristic to project commit"); } // create missing statistics if ( cdcoc == null ) { cdcoc = SpmFactory.eINSTANCE.createCriticalDefectChangeOrderCount(); cdcoc.setAccumulator(Accumulator.SUM); cdcoc.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); cdcoc.setName("Critical Defect Change Order Count)"); cdcoc.setScope(projectScope); cdcoc.setTrait(endProductQuality); cdcoc.setUnit("orders"); pc.getModelElement().add(cdcoc); CertWareLog.logInfo("Added critical defect change order statistic to project commit"); } if ( ndcoc == null ) { ndcoc = SpmFactory.eINSTANCE.createNormalDefectChangeOrderCount(); ndcoc.setAccumulator(Accumulator.SUM); ndcoc.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); ndcoc.setName("Normal Defect Change Order Count)"); ndcoc.setScope(projectScope); ndcoc.setTrait(endProductQuality); ndcoc.setUnit("orders"); pc.getModelElement().add(ndcoc); CertWareLog.logInfo("Added normal defect change order statistic to project commit"); } if ( icoc == null ) { icoc = SpmFactory.eINSTANCE.createImprovementChangeOrderCount(); icoc.setAccumulator(Accumulator.SUM); icoc.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); icoc.setName("Improvement Change Order Count)"); icoc.setScope(projectScope); icoc.setTrait(endProductQuality); icoc.setUnit("orders"); pc.getModelElement().add(icoc); CertWareLog.logInfo("Added improvement change order statistic to project commit"); } if ( nfcoc == null ) { nfcoc = SpmFactory.eINSTANCE.createNewFeatureChangeOrderCount(); nfcoc.setAccumulator(Accumulator.SUM); nfcoc.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); nfcoc.setName("New Feature Change Order Count)"); nfcoc.setScope(projectScope); nfcoc.setTrait(endProductQuality); nfcoc.setUnit("orders"); pc.getModelElement().add(nfcoc); CertWareLog.logInfo("Added new feature change order statistic to project commit"); } if ( tcoc == null ) { tcoc = SpmFactory.eINSTANCE.createTotalChangeOrderCount(); tcoc.setAccumulator(Accumulator.SUM); tcoc.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); tcoc.setName("Total Change Order Count)"); tcoc.setScope(projectScope); tcoc.setTrait(endProductQuality); tcoc.setUnit("orders"); pc.getModelElement().add(tcoc); CertWareLog.logInfo("Added total change order statistic to project commit"); } if ( cncoc == null ) { cncoc = SpmFactory.eINSTANCE.createCriticalAndNormalChangeOrderCount(); cncoc.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); cncoc.setName("Critical and Normal Change Order Count)"); cncoc.setScope(projectScope); cncoc.setTrait(endProductQuality); cncoc.setUnit("orders"); cncoc.setBaseMeasure1(cdcoc); cncoc.setBaseMeasure2(ndcoc); cncoc.setFunctor(ComputeMetricsHandler.FUNCTOR_ADD); pc.getModelElement().add(cncoc); CertWareLog.logInfo("Added total change order statistic to project commit"); } if ( bcs == null ) { bcs = SpmFactory.eINSTANCE.createBaselineCaseSizeMeasure(); bcs.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); bcs.setName("Baseline Case Size"); bcs.setScope(projectScope); bcs.setTrait(endProductQuality); bcs.setUnit("SLOC"); pc.getModelElement().add(bcs); CertWareLog.logInfo("Added baseline case size statistic to project commit"); } if ( tcs == null ) { tcs = SpmFactory.eINSTANCE.createTotalCaseSizeMeasure(); tcs.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); tcs.setName("Total Case Size"); tcs.setScope(projectScope); tcs.setTrait(endProductQuality); tcs.setUnit("SLOC"); pc.getModelElement().add(tcs); CertWareLog.logInfo("Added total case size statistic to project commit"); } if ( ut == null ) { ut = SpmFactory.eINSTANCE.createUsageTimeMeasure(); ut.setLibrary(ComputeMetricsHandler.LIBRARY_TAG); ut.setName("Usage Time"); ut.setScope(projectScope); ut.setTrait(endProductQuality); ut.setUnit("hrs"); pc.getModelElement().add(ut); CertWareLog.logInfo("Added usage time statistic to project commit"); } // at this point we have all statistics, and the categories and metrics are in the commit // compute statistics, putting the measurement values into the front of the measurement list int baselinedCount = 0; int currentCount = 0; int criticalChangeCount = 0; int normalChangeCount = 0; int improvementChangeCount = 0; int newFeatureChangeCount = 0; int totalChangeOrderCount = 0; double totalUsageTime = 0.0; // the project commit ID is presumed to be the first annotation // might change his index to a preference later... String commitId = null; if ( pc.getAnnotation() != null && pc.getAnnotation().size() > 0 ) commitId = pc.getAnnotation().get(0).getText(); // for each history artifact set in the list for ( CommitHistory ch : histories ) { for ( ArtifactCommit ac : ch.getCommitRecord() ) { // match commit ID test String changeCommitId = ac.getCommitIdentifier(); if ( matchCommitIds ) { if ( commitId == null || changeCommitId == null ) { continue; } if ( commitId.equals(changeCommitId) == false ) { continue; } // drop through is a match } // critical changes CriticalDefectChangeOrders cc = ac.getAllCriticalDefectChangeOrders(); if ( cc != null ) criticalChangeCount += cc.getValue(); // normal changes NormalDefectChangeOrders nc = ac.getAllNormalDefectChangeOrders(); if ( nc != null ) normalChangeCount += nc.getValue(); // improvement changes ImprovementChangeOrders ic = ac.getAllImprovementChangeOrders(); if ( ic != null ) improvementChangeCount += ic.getValue(); // new feature changes NewFeatureChangeOrders fc = ac.getAllNewFeatureChangeOrders(); if ( fc != null ) newFeatureChangeCount += fc.getValue(); // total change orders TotalChangeOrders xo = ac.getAllTotalChangeOrders(); if ( xo != null ) totalChangeOrderCount += xo.getValue(); // usage time totalUsageTime += ac.getUsageTime(); // baselined and current lines baselinedCount += ac.getAllBaselinedLineCount(); currentCount += ac.getAllCurrentLineCount(); } // artifact commits } // commit histories // compute the combined metric ComputeMetricsHandler computeMetrics = new ComputeMetricsHandler(); // insert into statistics as measurements // one of them is a combined metric computeMetrics.setStatistic(tcoc, totalChangeOrderCount, clearMeasurements); computeMetrics.setStatistic(nfcoc, newFeatureChangeCount, clearMeasurements); computeMetrics.setStatistic(icoc, improvementChangeCount, clearMeasurements); computeMetrics.setStatistic(ndcoc, normalChangeCount, clearMeasurements); computeMetrics.setStatistic(cdcoc, criticalChangeCount, clearMeasurements); computeMetrics.setStatistic(ut, totalUsageTime, clearMeasurements); computeMetrics.computeMetric(cncoc, clearMeasurements); // line counts, if selected if ( includeLineCounts ) { computeMetrics.setStatistic(tcs, currentCount, clearMeasurements); computeMetrics.setStatistic(bcs, baselinedCount, clearMeasurements); } // report completion String name = getName(pc); CertWareLog.logInfo(String.format("%s %s","Computed change order statistics for commit",name)); return true; } /** * Finds the project scope element in the project model container. * @param pc project commit, presumed to be contained by a project model element * @return project scope element or null if not found searching the project model */ private ProjectScope findProjectScopeFromCommit(ProjectCommit pc) { ProjectModel pm = (ProjectModel)pc.eContainer(); TreeIterator<?> ti = pm.eAllContents(); while( ti.hasNext() ) { EObject eo = (EObject)ti.next(); if ( eo instanceof ProjectScope ) { return (ProjectScope)eo; } } return null; } /** * Open a selected SCO file. * @param ifile file selection from resources * @return artifact list or null */ private CommitHistory loadOrdersFromFile(IFile ifile) { try { // load the SCO file through the EMF resource set implementation ResourceSet resourceSet = new ResourceSetImpl(); Resource resource = resourceSet.getResource( URI.createPlatformResourceURI(ifile.getFullPath().toString(), true), true); CommitHistory documentRoot = (CommitHistory)resource.getContents().get(0); if ( documentRoot != null ) { return documentRoot; } } catch( Exception exception ) { CertWareLog.logError(String.format("%s %s", "Document root null loading" + ifile.getName()), exception); } return null; } }