blob: 8724b2f5ebd871707b602760130c6cb46e9600d7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2007 Boeing.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Boeing - initial API and implementation
*******************************************************************************/
package org.eclipse.osee.define.traceability;
import java.io.File;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.osee.define.internal.Activator;
import org.eclipse.osee.define.traceability.TraceUnitExtensionManager.TraceHandler;
import org.eclipse.osee.define.traceability.data.RequirementData;
import org.eclipse.osee.define.traceability.data.TraceMark;
import org.eclipse.osee.framework.core.data.IArtifactType;
import org.eclipse.osee.framework.core.data.IOseeBranch;
import org.eclipse.osee.framework.core.enums.CoreAttributeTypes;
import org.eclipse.osee.framework.jdk.core.type.CountingMap;
import org.eclipse.osee.framework.jdk.core.type.HashCollection;
import org.eclipse.osee.framework.jdk.core.type.OseeArgumentException;
import org.eclipse.osee.framework.jdk.core.type.OseeCoreException;
import org.eclipse.osee.framework.jdk.core.type.OseeStateException;
import org.eclipse.osee.framework.jdk.core.type.Pair;
import org.eclipse.osee.framework.jdk.core.util.Collections;
import org.eclipse.osee.framework.jdk.core.util.Lib;
import org.eclipse.osee.framework.jdk.core.util.Strings;
import org.eclipse.osee.framework.jdk.core.util.io.CharBackedInputStream;
import org.eclipse.osee.framework.jdk.core.util.io.xml.ExcelXmlWriter;
import org.eclipse.osee.framework.jdk.core.util.io.xml.ISheetWriter;
import org.eclipse.osee.framework.logging.OseeLog;
import org.eclipse.osee.framework.plugin.core.util.AIFile;
import org.eclipse.osee.framework.plugin.core.util.OseeData;
import org.eclipse.osee.framework.skynet.core.artifact.Artifact;
import org.eclipse.osee.framework.skynet.core.artifact.Attribute;
import org.eclipse.osee.framework.skynet.core.word.WordUtil;
import org.eclipse.osee.ote.define.artifacts.ArtifactTestRunOperator;
import org.eclipse.swt.program.Program;
/**
* @author Ryan D. Brooks
*/
public class ScriptTraceabilityOperation extends TraceabilityProviderOperation {
private static final Pattern filePattern = Pattern.compile(".*\\.(java|ada|ads|adb|c|h)");
private static final Matcher structuredRequirementMatcher = Pattern.compile("\\[?(\\{[^\\}]+\\})(.*)").matcher("");
private static final Matcher embeddedVolumeMatcher = Pattern.compile("\\{\\d+ (.*)\\}[ .]*").matcher("");
private static final Matcher stripTrailingReqNameMatcher = Pattern.compile("(\\}|\\])(.*)").matcher("");
private static final Matcher nonWordMatcher = Pattern.compile("[^A-Z_0-9]").matcher("");
private static final Matcher subsystemMatcher = Pattern.compile("(\\w*)\\.ss").matcher("");
private static final Matcher gitSubsystemMatcher = Pattern.compile("\\w*\\.ofp\\\\(\\w*)").matcher("");
private final Collection<TraceHandler> traceHandlers;
private final File file;
private final RequirementData requirementData;
private final ArrayList<String> noTraceabilityFiles = new ArrayList<>(200);
private final CountingMap<Artifact> reqsTraceCounts = new CountingMap<>();
private final HashCollection<Artifact, String> requirementToCodeUnitsMap =
new HashCollection<>(false, LinkedHashSet.class);
private final HashSet<String> codeUnits = new HashSet<>();
private final CharBackedInputStream charBak;
private final ISheetWriter excelWriter;
private int pathPrefixLength;
private final boolean writeOutResults;
private final boolean isGitBased;
private ScriptTraceabilityOperation(RequirementData requirementData, File file, boolean writeOutResults, Collection<TraceHandler> traceHandlers, boolean isGitBased) throws IOException {
super("Importing Traceability", Activator.PLUGIN_ID);
this.file = file;
this.requirementData = requirementData;
this.writeOutResults = writeOutResults;
this.traceHandlers = traceHandlers;
this.isGitBased = isGitBased;
charBak = new CharBackedInputStream();
excelWriter = new ExcelXmlWriter(charBak.getWriter());
}
public ScriptTraceabilityOperation(File file, IOseeBranch branch, boolean writeOutResults, Collection<TraceHandler> traceHandlers, boolean isGitBased) throws IOException {
this(new RequirementData(branch), file, writeOutResults, traceHandlers, isGitBased);
}
public ScriptTraceabilityOperation(File file, IOseeBranch branch, boolean writeOutResults, Collection<? extends IArtifactType> types, boolean withInheritance, Collection<TraceHandler> traceHandlers, boolean isGitBased) throws IOException {
this(new RequirementData(branch, types, withInheritance), file, writeOutResults, traceHandlers, isGitBased);
}
@Override
protected void doWork(IProgressMonitor monitor) throws Exception {
monitor.worked(1);
requirementData.initialize(monitor);
if (writeOutResults) {
excelWriter.startSheet("srs <--> code units", 6);
excelWriter.writeRow("Req in DB", "Subsystem", "Code Unit", "Requirement Name",
"Requirement Trace Mark in Code", "Trace Mark Match");
}
if (file.isFile()) {
for (String path : Lib.readListFromFile(file, true)) {
monitor.subTask(path);
handleDirectory(new File(path), traceHandlers);
checkForCancelledStatus(monitor);
}
} else if (file.isDirectory()) {
handleDirectory(file, traceHandlers);
} else {
throw new OseeStateException("Invalid path [%s]", file.getCanonicalPath());
}
checkForCancelledStatus(monitor);
if (writeOutResults) {
excelWriter.endSheet();
writeNoTraceFilesSheet();
writeTraceCountsSheet();
excelWriter.endWorkbook();
IFile iFile = OseeData.getIFile("CodeUnit_To_SRS_Trace.xml");
AIFile.writeToFile(iFile, charBak);
Program.launch(iFile.getLocation().toOSString());
}
}
private void handleDirectory(File directory, Collection<TraceHandler> traceHandlers) throws IOException, OseeCoreException {
if (directory == null || directory.getParentFile() == null) {
throw new OseeArgumentException("The path [%s] is invalid.", directory);
}
pathPrefixLength = directory.getParentFile().getAbsolutePath().length();
for (File sourceFile : Lib.recursivelyListFiles(directory, filePattern)) {
CharBuffer buffer = Lib.fileToCharBuffer(sourceFile);
Collection<TraceMark> tracemarks = new LinkedList<>();
for (TraceHandler handler : traceHandlers) {
Collection<TraceMark> marks = handler.getParser().getTraceMarks(buffer);
tracemarks.addAll(marks);
}
int matchCount = 0;
String relativePath = sourceFile.getPath().substring(pathPrefixLength);
codeUnits.add(relativePath);
for (TraceMark traceMark : tracemarks) {
handelReqTrace(relativePath, traceMark, sourceFile);
matchCount++;
}
if (matchCount == 0) {
noTraceabilityFiles.add(relativePath);
}
}
}
private void writeNoTraceFilesSheet() throws IOException {
excelWriter.startSheet("no match files", 1);
for (String path : noTraceabilityFiles) {
excelWriter.writeRow(path);
}
excelWriter.endSheet();
}
private void writeTraceCountsSheet() throws IOException, OseeCoreException {
excelWriter.startSheet("trace counts", 4);
excelWriter.writeRow("SRS Requirement from Database", "Trace Count", "Partitions", "Artifact Type");
excelWriter.writeRow("% requirement coverage", null, "=1-COUNTIF(C2,&quot;0&quot;)/COUNTA(C2)", null);
for (Artifact artifact : requirementData.getDirectRequirements()) {
excelWriter.writeRow(artifact.getName(), String.valueOf(reqsTraceCounts.get(artifact)),
Collections.toString(",", artifact.getAttributesToStringList(CoreAttributeTypes.Partition)),
artifact.getArtifactType());
}
excelWriter.endSheet();
}
private Pair<String, String> getStructuredRequirement(String requirementMark) {
Pair<String, String> toReturn = null;
structuredRequirementMatcher.reset(requirementMark);
if (structuredRequirementMatcher.matches() != false) {
String primary = structuredRequirementMatcher.group(1);
String secondary = structuredRequirementMatcher.group(2);
if (Strings.isValid(primary) != false) {
toReturn = new Pair<>(primary, secondary);
}
}
return toReturn;
}
public String getCanonicalRequirementName(String requirementMark) {
String canonicalReqReference = requirementMark;
if (Strings.isValid(requirementMark) != false) {
canonicalReqReference = requirementMark.toUpperCase();
embeddedVolumeMatcher.reset(canonicalReqReference);
if (embeddedVolumeMatcher.find()) {
canonicalReqReference = embeddedVolumeMatcher.group(1);
}
// Added to strip trailing artifact descriptive names } ... or ] ....
stripTrailingReqNameMatcher.reset(canonicalReqReference);
if (stripTrailingReqNameMatcher.find()) {
String trail = stripTrailingReqNameMatcher.group(2);
if (Strings.isValid(trail) && !trail.startsWith(".")) {
canonicalReqReference = canonicalReqReference.substring(0, stripTrailingReqNameMatcher.start(1) + 1);
}
}
nonWordMatcher.reset(canonicalReqReference);
canonicalReqReference = nonWordMatcher.replaceAll("");
}
return canonicalReqReference;
}
private String getSubsystem(String source, Matcher matcher) {
String subSystem = null;
matcher.reset(source);
if (matcher.find()) {
subSystem = matcher.group(1);
subSystem = subSystem.toUpperCase();
} else {
subSystem = "no valid subsystem found";
}
return subSystem;
}
private void handelReqTrace(String path, TraceMark traceMark, File sourceFile) throws OseeCoreException, IOException {
Artifact reqArtifact = null;
String foundStr;
String subSystem = null;
String textContent = null;
boolean traceMatch = false;
subSystem = (isGitBased) ? getSubsystem(sourceFile.getPath(),
gitSubsystemMatcher) : getSubsystem(sourceFile.getPath(), subsystemMatcher);
if (traceMark.getTraceType().equals("Uses")) {
foundStr = "invalid trace mark";
} else {
reqArtifact = requirementData.getRequirementFromTraceMark(traceMark.getRawTraceMark());
if (reqArtifact == null) {
Pair<String, String> structuredRequirement = getStructuredRequirement(traceMark.getRawTraceMark());
if (structuredRequirement != null) {
reqArtifact = requirementData.getRequirementFromTraceMark(structuredRequirement.getFirst());
if (reqArtifact == null) {
foundStr = "no match in DB";
} else {
// for local data and procedures search requirement text for traceMark
// example local data [{SUBSCRIBER}.ID] and example procedure {CURSOR_ACKNOWLEDGE}.NORMAL
//There is no WordTemplateContent in a button requirement so we need to verify it exists
//If its not there we need to render the button requirement in Word and pull out the body.
if (reqArtifact.getAttributeCount(CoreAttributeTypes.WordTemplateContent) > 0) {
textContent = WordUtil.textOnly(
reqArtifact.getSoleAttributeValue(CoreAttributeTypes.WordTemplateContent, "")).toUpperCase();
} else {
List<Attribute<?>> attributes = reqArtifact.getAttributes();
for (Attribute<?> attribute : attributes) {
textContent = textContent + attribute.toString();
}
}
if (textContent.contains(structuredRequirement.getSecond()) || textContent.contains(
getCanonicalRequirementName(structuredRequirement.getSecond()))) {
foundStr = "req body match";
} else {
foundStr = "req name match/element missing in body";
}
}
} else {
foundStr = "no match in DB";
}
} else {
foundStr = fullMatch(reqArtifact);
List<String> partitions = reqArtifact.getAttributesToStringList(CoreAttributeTypes.Partition);
if (partitions.contains(subSystem)) {
traceMatch = true;
}
}
}
String name = null;
if (reqArtifact != null) {
name = reqArtifact.getName();
String inspection = getInspectionQual(reqArtifact);
if (Strings.isValid(inspection)) {
requirementToCodeUnitsMap.put(reqArtifact, inspection);
}
requirementToCodeUnitsMap.put(reqArtifact, path);
}
if (writeOutResults) {
excelWriter.writeRow(foundStr, subSystem, path, name, traceMark, traceMatch);
}
}
private String fullMatch(Artifact reqArtifact) {
reqsTraceCounts.put(reqArtifact);
return "full match";
}
@Override
public HashCollection<Artifact, String> getRequirementToCodeUnitsMap() {
return requirementToCodeUnitsMap;
}
/**
* @return the codeUnits
*/
@Override
public HashSet<String> getCodeUnits() {
return codeUnits;
}
@Override
public RequirementData getRequirementData() {
return requirementData;
}
@Override
public Collection<Artifact> getTestUnitArtifacts(Artifact requirement) {
Collection<Artifact> toReturn = new HashSet<>();
Collection<String> scriptNames = requirementToCodeUnitsMap.getValues(requirement);
if (scriptNames != null) {
for (String script : scriptNames) {
Artifact testScript;
try {
testScript = ArtifactTestRunOperator.getTestScriptFetcher().getNewArtifact(requirement.getBranch());
testScript.setName(script);
toReturn.add(testScript);
} catch (OseeCoreException ex) {
OseeLog.log(Activator.class, Level.SEVERE, ex);
}
}
}
return toReturn;
}
@Override
public Artifact getTestUnitByName(String name) {
return null;
}
}