/*******************************************************************************
* Copyright (c) 2009 Thales Corporate Services SAS *
* Author : Gregory Boissinot *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy *
* of this software and associated documentation files (the "Software"), to deal*
* in the Software without restriction, including without limitation the rights *
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell *
* copies of the Software, and to permit persons to whom the Software is *
* furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,*
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN *
* THE SOFTWARE. *
*******************************************************************************/
package com.thalesgroup.hudson.plugins.cppcheck;
import hudson.model.AbstractBuild;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import com.thalesgroup.hudson.plugins.cppcheck.model.CppcheckFile;
import com.thalesgroup.hudson.plugins.cppcheck.model.CppcheckWorkspaceFile;
import de.java2html.converter.JavaSource2HTMLConverter;
import de.java2html.javasource.JavaSource;
import de.java2html.javasource.JavaSourceParser;
import de.java2html.options.JavaSourceConversionOptions;
public class CppcheckSource implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Offset of the source code generator. After this line the actual source file lines start.
*/
protected static final int SOURCE_GENERATOR_OFFSET = 13;
/**
* Color for the first (primary) annotation range.
*/
private static final String MESSAGE_COLOR = "#FCAF3E";
/**
* The current build as owner of this object.
*/
private final AbstractBuild<?, ?> owner;
/**
* The cppcheck source file in the workspace to be shown.
*/
private final CppcheckWorkspaceFile cppcheckWorkspaceFile;
/**
* The rendered source file.
*/
private String sourceCode = StringUtils.EMPTY;
/**
* Creates a new instance of this source code object.
*
* @param owner the current build as owner of this object
* @param cppcheckWorkspaceFile the abstract workspace file
*/
public CppcheckSource(final AbstractBuild<?, ?> owner, CppcheckWorkspaceFile cppcheckWorkspaceFile) {
this.owner = owner;
this.cppcheckWorkspaceFile = cppcheckWorkspaceFile;
buildFileContent();
}
/**
* Builds the file content.
*/
private void buildFileContent() {
InputStream is = null;
try {
File tempFile = new File(cppcheckWorkspaceFile.getTempName(owner));
if (tempFile.exists()) {
is = new FileInputStream(tempFile);
} else {
if (cppcheckWorkspaceFile.getFileName() == null) {
throw new IOException("The file doesn't exist.");
}
File file = new File(cppcheckWorkspaceFile.getFileName());
if (!file.exists()) {
throw new IOException("Can't access the file: " + file.toURI());
}
is = new FileInputStream(file);
}
splitSourceFile(highlightSource(is));
}
catch (IOException exception) {
sourceCode = "Can't read file: " + exception.getLocalizedMessage();
}
catch (RuntimeException re) {
sourceCode = "Problem for display the source code content: " + re.getLocalizedMessage();
}
finally {
IOUtils.closeQuietly(is);
}
}
/**
* Splits the source code into three blocks: the line to highlight and the
* source code before and after this line.
*
* @param sourceFile the source code of the whole file as rendered HTML string
*/
private final void splitSourceFile(final String sourceFile) {
StringBuilder output = new StringBuilder(sourceFile.length());
CppcheckFile cppcheckFile = cppcheckWorkspaceFile.getCppcheckFile();
LineIterator lineIterator = IOUtils.lineIterator(new StringReader(sourceFile));
int lineNumber = 1;
//---header
while (lineNumber < SOURCE_GENERATOR_OFFSET) {
copyLine(output, lineIterator);
lineNumber++;
}
lineNumber = 1;
//---iterate before the error line
while (lineNumber < cppcheckFile.getLineNumber()) {
copyLine(output, lineIterator);
lineNumber++;
}
output.append("</code>\n");
//---Error message
output.append("</td></tr>\n");
output.append("<tr><td bgcolor=\"");
appendRangeColor(output);
output.append("\">\n");
output.append("<div tooltip=\"");
outputEscaped(output, cppcheckFile.getCppCheckId() + ":" + cppcheckFile.getMessage());
output.append("\" nodismiss=\"\">\n");
output.append("<code><b>\n");
//The current line error
copyLine(output, lineIterator);
lineNumber++;
//End of the code
output.append("</b></code>\n");
output.append("</div>\n");
output.append("</td></tr>\n");
output.append("<tr><td>\n");
output.append("<code>\n");
while (lineIterator.hasNext()) {
copyLine(output, lineIterator);
}
output.append("</code>\n");
output.append("</td></tr>\n");
sourceCode = output.toString();
}
/**
* Writes the message to the output stream (with escaped HTML).
*
* @param output the output to write to
* @param message the message to write
*/
private void outputEscaped(final StringBuilder output, final String message) {
output.append(StringEscapeUtils.escapeHtml(message));
}
/**
* Appends the message color.
*
* @param output the output to append the color
*/
private void appendRangeColor(final StringBuilder output) {
output.append(MESSAGE_COLOR);
}
/**
* Copies the next line of the input to the output.
*
* @param output output
* @param lineIterator input
*/
private void copyLine(final StringBuilder output, final LineIterator lineIterator) {
output.append(lineIterator.nextLine());
output.append("\n");
}
/**
* Highlights the specified source and returns the result as an HTML string.
*
* @param file the source file to highlight
* @return the source as an HTML string
* @throws IOException
*/
public final String highlightSource(final InputStream file) throws IOException {
JavaSource source = new JavaSourceParser().parse(new InputStreamReader(file));
JavaSource2HTMLConverter converter = new JavaSource2HTMLConverter();
StringWriter writer = new StringWriter();
JavaSourceConversionOptions options = JavaSourceConversionOptions.getDefault();
options.setShowLineNumbers(true);
options.setAddLineAnchors(true);
converter.convert(source, options, writer);
return writer.toString();
}
/**
* Retrieve the source code for the cppcheck source file.
*
* @return the source code content as a String object
*/
@SuppressWarnings("unused")
public String getSourceCode() {
return sourceCode;
}
/**
* Returns the abstract Cppcheck workspace file.
*
* @return the workspace file
*/
@SuppressWarnings("unused")
public CppcheckWorkspaceFile getCppcheckWorkspaceFile() {
return cppcheckWorkspaceFile;
}
}