Skip to content

Commit

Permalink
Merge pull request #14 from mixalturek/master
Browse files Browse the repository at this point in the history
[JENKINS-18109] Show all violations highlighted on a single page
  • Loading branch information
mixalturek committed Apr 29, 2014
2 parents 1dba0fe + 1f0faed commit cf2d85a
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 68 deletions.
Expand Up @@ -150,7 +150,7 @@ private void splitSourceFile(final String sourceFile) {
output.append("\">\n");

output.append("<div tooltip=\"");
outputEscaped(output, cppcheckFile.getCppCheckId() + ":" + cppcheckFile.getMessage());
outputEscaped(output, cppcheckFile.getCppCheckId() + ": " + cppcheckFile.getMessage());
output.append("\" nodismiss=\"\">\n");
output.append("<code><b>\n");

Expand Down
Expand Up @@ -24,6 +24,8 @@
package com.thalesgroup.hudson.plugins.cppcheck.model;

import hudson.model.ModelObject;

import org.apache.commons.lang.StringEscapeUtils;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;

Expand Down Expand Up @@ -104,6 +106,10 @@ public void setCppCheckId(String cppCheckId) {
public String getMessage() {
return message;
}

public String getMessageHtml() {
return StringEscapeUtils.escapeHtml(message);
}

public void setMessage(String message) {
this.message = message;
Expand Down
Expand Up @@ -103,18 +103,6 @@ public final String getFileName() {
return fileName;
}

/**
* Returns the name of this file, allow wrap of long paths in a HTML page.
*
* @return the name of this file with <wbr /> element after each slash
*/
public final String getFileNameHtmlWrap() {
if(fileName == null)
return "";

return fileName.replace("/", "/<wbr />");
}

public String getTempName(final AbstractBuild<?, ?> owner) {
if (fileName != null) {
return owner.getRootDir().getAbsolutePath() + "/"
Expand Down
41 changes: 37 additions & 4 deletions src/main/java/org/jenkinsci/plugins/cppcheck/CppcheckResult.java
Expand Up @@ -33,14 +33,20 @@ public class CppcheckResult implements Serializable {
private static final long serialVersionUID = 2L;

/**
* The Cppcheck report. Backward compatibility with version 1.14 and less.
* The Cppcheck report.
*
* @deprecated Only for backward compatibility with version 1.14 and less.
*/
@Deprecated
private transient CppcheckReport report;

/**
* The Cppcheck container with all source files. Backward compatibility
* with version 1.14 and less.
* The Cppcheck container with all source files.
*
* @deprecated Only for backward compatibility with version 1.14 and less.
* @see #lazyLoadSourceContainer()
*/
@Deprecated
private transient CppcheckSourceContainer cppcheckSourceContainer;

/**
Expand Down Expand Up @@ -130,9 +136,18 @@ public CppcheckSourceContainer getCppcheckSourceContainer() {
*/
public Object getDynamic(final String link, final StaplerRequest request,
final StaplerResponse response) throws IOException {
if (link.equals("source.all")) {
if (!owner.getProject().getACL().hasPermission(Item.WORKSPACE)) {
response.sendRedirect2("nosourcepermission");
return null;
}

if (link.startsWith("source.")) {
Collection<CppcheckWorkspaceFile> files = diffCurrentAndPrevious();
int before = parseIntWithDefault(request.getParameter("before"), 5);
int after = parseIntWithDefault(request.getParameter("after"), 5);

return new CppcheckSourceAll(owner, files, before, after);
} else if (link.startsWith("source.")) {
if (!owner.getProject().getACL().hasPermission(Item.WORKSPACE)) {
response.sendRedirect2("nosourcepermission");
return null;
Expand All @@ -155,6 +170,24 @@ public Object getDynamic(final String link, final StaplerRequest request,
return null;
}

/**
* Parse integer.
*
* @param str
* the input string
* @param defaultValue
* the default value returned on error
* @return the parsed value or default value on error
* @see Integer#parseInt(String)
*/
private int parseIntWithDefault(String str, int defaultValue) {
try {
return Integer.parseInt(str);
} catch(NumberFormatException e) {
return defaultValue;
}
}

/**
* Gets the previous Cppcheck result for the build result.
*
Expand Down
143 changes: 143 additions & 0 deletions src/main/java/org/jenkinsci/plugins/cppcheck/CppcheckSourceAll.java
@@ -0,0 +1,143 @@
package org.jenkinsci.plugins.cppcheck;

import hudson.model.AbstractBuild;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;

import com.thalesgroup.hudson.plugins.cppcheck.model.CppcheckWorkspaceFile;

/**
* Show all violations highlighted on a single page.
*
* @author Michal Turek
* @since 1.16
*/
public class CppcheckSourceAll {
/** The related build. */
private final AbstractBuild<?, ?> owner;

/** The files to show. */
private final Collection<CppcheckWorkspaceFile> files;

/** Number of lines to show before the highlighted line. */
private final int linesBefore;

/** Number of lines to show after the highlighted line. */
private final int linesAfter;

/**
* Constructor.
*
* @param owner
* the related build
* @param files
* the files to show
* @param linesBefore
* number of lines to show before the highlighted line
* @param linesAfter
* number of lines to show after the highlighted line
*/
public CppcheckSourceAll(AbstractBuild<?, ?> owner,
Collection<CppcheckWorkspaceFile> files, int linesBefore,
int linesAfter) {
this.owner = owner;
this.files = files;
this.linesBefore = linesBefore;
this.linesAfter = linesAfter;
}

public AbstractBuild<?, ?> getOwner() {
return owner;
}

public Collection<CppcheckWorkspaceFile> getFiles() {
return files;
}

public int getLinesBefore() {
return linesBefore;
}

public int getLinesAfter() {
return linesAfter;
}

/**
* Get specified lines of source code from the file.
*
* @param file
* the input file
* @return the related lines of code with HTML formatting
*/
public String getSourceCode(CppcheckWorkspaceFile file) {
File tempFile = new File(file.getTempName(owner));

if (!tempFile.exists()) {
return "Can't read file: " + tempFile.getAbsolutePath();
}

BufferedReader reader = null;

try {
reader = new BufferedReader(new FileReader(tempFile));
return getRelatedLines(reader, file.getCppcheckFile()
.getLineNumber());
} catch (FileNotFoundException e) {
return "Can't read file: " + e;
} catch (IOException e) {
return "Reading file failed: " + e;
} finally {
IOUtils.closeQuietly(reader);
}
}

/**
* Get specified lines from a stream.
*
* @param reader
* the input stream
* @param lineNumber
* the base line
* @return the lines with HTML formatting
* @throws IOException
* if something fails
*/
private String getRelatedLines(BufferedReader reader, int lineNumber)
throws IOException {
final int start = (lineNumber > linesBefore) ? lineNumber - linesBefore : 1;
final int end = lineNumber + linesAfter;
final String numberFormat = "%0" + String.valueOf(end).length() + "d";

StringBuilder builder = new StringBuilder();
int current = 1;
String line = "";

while ((line = reader.readLine()) != null && current <= end) {
if (current >= start) {
if (current == lineNumber) {
builder.append("<div class=\"line highlighted\">");
} else {
builder.append("<div class=\"line\">");
}

builder.append("<span class=\"lineNumber\">");
builder.append(String.format(numberFormat, current));
builder.append("</span> ");// The space separates line number and code
builder.append(StringEscapeUtils.escapeHtml(line));
builder.append("</div>\n");
}

++current;
}

return builder.toString();
}
}
Expand Up @@ -32,7 +32,7 @@
<l:main-panel>
<h1>${%Cppcheck Results}</h1>

<h2>${%sourcedetail.header(it.cppcheckWorkspaceFile.fileNameHtmlWrap)}</h2>
<h2>${%sourcedetail.header(it.cppcheckWorkspaceFile.cppcheckFile.fileName)}</h2>

${it.sourceCode}
</l:main-panel>
Expand Down
Expand Up @@ -3,58 +3,58 @@

<j:set var="cachedContainer" value="${it.diffCurrentAndPrevious()}"/>

<j:if test="${cachedContainer != null}">
<h2>${%Details}</h2>

<h2>${%Details}</h2>
<style type="text/css">
#cppcheckDetails { width: auto; }
#cppcheckDetails td { white-space: normal; }
#cppcheckDetails .new { background-color: #FFC8C8; }
#cppcheckDetails .solved { background-color: #C8FFC8; }
#cppcheckDetails .unchanged { }
</style>

<p><a href="source.all?before=5&amp;after=5">
${%Show all violations highlighted on a single page.}
</a></p>

<style type="text/css">
#cppcheckDetails { width: auto; }
#cppcheckDetails td { white-space: normal; }
#cppcheckDetails .new { background-color: #FFC8C8; }
#cppcheckDetails .solved { background-color: #C8FFC8; }
#cppcheckDetails .unchanged { }
</style>
<table class="pane sortable" id="cppcheckDetails">
<thead>
<tr>
<td class="pane-header">${%State}</td>
<td class="pane-header">${%File}</td>
<td class="pane-header">${%Line}</td>
<td class="pane-header">${%Severity}</td>
<td class="pane-header">${%Type}</td>
<td class="pane-header">${%Message}</td>
</tr>
</thead>
<tbody>
<j:forEach var="elt" items="${cachedContainer}">
<j:set var="cppcheckFile" value="${elt.cppcheckFile}"/>

<table class="pane sortable" id="cppcheckDetails">
<thead>
<tr>
<td class="pane-header">${%State}</td>
<td class="pane-header">${%File}</td>
<td class="pane-header">${%Line}</td>
<td class="pane-header">${%Severity}</td>
<td class="pane-header">${%Type}</td>
<td class="pane-header">${%Message}</td>
<tr class="${elt.diffState.css}">
<td class="pane">${elt.diffState.text}</td>
<td class="pane">
<j:if test="${elt.isSourceIgnored()}">
${cppcheckFile.fileName}
</j:if>
<j:if test="${not elt.isSourceIgnored()}">
<a href="source.${cppcheckFile.key}">${cppcheckFile.fileName}</a>
</j:if>
</td>
<td class="pane" data="${cppcheckFile.lineNumber}">
<j:if test="${elt.isSourceIgnored()}">
${cppcheckFile.lineNumberString}
</j:if>
<j:if test="${not elt.isSourceIgnored()}">
<a href="source.${cppcheckFile.key}#${cppcheckFile.linkLineNumber}">${cppcheckFile.lineNumberString}</a>
</j:if>
</td>
<td class="pane">${cppcheckFile.severity}</td>
<td class="pane">${cppcheckFile.cppCheckId}</td>
<td class="pane">${cppcheckFile.messageHtml}</td>
</tr>
</thead>
<tbody>
<j:forEach var="elt" items="${cachedContainer}">
<j:set var="cppcheckfile" value="${elt.cppcheckFile}"/>

<tr class="${elt.diffState.css}">
<td class="pane">${elt.diffState.text}</td>
<td class="pane">
<j:if test="${elt.isSourceIgnored()}">
${cppcheckfile.fileName}
</j:if>
<j:if test="${not elt.isSourceIgnored()}">
<a href="source.${cppcheckfile.key}">${cppcheckfile.fileName}</a>
</j:if>
</td>
<td class="pane" data="${cppcheckfile.lineNumber}">
<j:if test="${elt.isSourceIgnored()}">
${cppcheckfile.lineNumberString}
</j:if>
<j:if test="${not elt.isSourceIgnored()}">
<a href="source.${cppcheckfile.key}#${cppcheckfile.linkLineNumber}">${cppcheckfile.lineNumberString}</a>
</j:if>
</td>
<td class="pane">${cppcheckfile.severity}</td>
<td class="pane">${cppcheckfile.cppCheckId}</td>
<td class="pane">${cppcheckfile.message}</td>
</tr>
</j:forEach>
</tbody>
</table>

</j:if>
</j:forEach>
</tbody>
</table>
</j:jelly>

0 comments on commit cf2d85a

Please sign in to comment.