package hudson.plugins.warnings.parser;
import hudson.plugins.analysis.util.JavaPackageDetector;
import hudson.plugins.analysis.util.model.FileAnnotation;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
/**
* Parses an input stream for compiler warnings using the provided regular expression.
*
* @author Ulli Hafner
*/
public abstract class RegexpParser implements WarningsParser {
/** Used to define a false positive warnings that should be excluded after the regular expression scan. */
protected static final Warning FALSE_POSITIVE = new Warning(StringUtils.EMPTY, 0, StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY);
/** Warning classification. */
protected static final String DEPRECATION = "Deprecation";
/** Warning classification. */
protected static final String PROPRIETARY_API = "Proprietary API";
/** Pattern of compiler warnings. */
private final Pattern pattern;
/** Name of this parser. */
private String name;
/**
* Creates a new instance of <code>RegexpParser</code>. Uses a single line matcher.
*
* @param warningPattern
* pattern of compiler warnings.
* @param name
* name of the parser
*/
public RegexpParser(final String warningPattern, final String name) {
this(warningPattern, false, name);
}
/**
* Creates a new instance of <code>RegexpParser</code>.
*
* @param warningPattern
* pattern of compiler warnings.
* @param useMultiLine
* Enables multi line mode. In multi line mode the expressions
* <tt>^</tt> and <tt>$</tt> match just after or just before,
* respectively, a line terminator or the end of the input
* sequence. By default these expressions only match at the
* beginning and the end of the entire input sequence.
* @param name
* name of the parser
*/
public RegexpParser(final String warningPattern, final boolean useMultiLine, final String name) {
this.name = name;
if (useMultiLine) {
pattern = Pattern.compile(warningPattern, Pattern.MULTILINE);
}
else {
pattern = Pattern.compile(warningPattern);
}
}
/**
* Parses the specified string content and creates annotations for each found warning.
*
* @param content the content to scan
* @param warnings the found annotations
*/
protected void findAnnotations(final String content, final List<FileAnnotation> warnings) {
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
Warning warning = createWarning(matcher);
if (warning != FALSE_POSITIVE) { // NOPMD
detectPackageName(warning);
warnings.add(warning);
}
}
}
/**
* Detects the package name for the specified warning.
*
* @param warning the warning
*/
private void detectPackageName(final Warning warning) {
if (!warning.hasPackageName()) {
String packageName = new JavaPackageDetector().detectPackageName(warning.getFileName());
warning.setPackageName(packageName);
}
}
/** {@inheritDoc} */
@Override
public String toString() {
return getName();
}
/** {@inheritDoc} */
public String getName() {
return name;
}
/**
* Sets the name to the specified value.
*
* @param name the value to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* Creates a new annotation for the specified pattern. This method is called
* for each matching line in the specified file. If a match is a false
* positive, then you can return the constant {@link #FALSE_POSITIVE} to
* ignore this warning.
*
* @param matcher
* the regular expression matcher
* @return a new annotation for the specified pattern
*/
protected abstract Warning createWarning(final Matcher matcher);
/**
* Converts a string line number to an integer value. If the string is not a valid line number,
* then 0 is returned which indicates a warning at the top of the file.
*
* @param lineNumber the line number (as a string)
* @return the line number
*/
protected final int getLineNumber(final String lineNumber) {
if (StringUtils.isNotBlank(lineNumber)) {
try {
return Integer.parseInt(lineNumber);
}
catch (NumberFormatException exception) {
// ignore and return 0
}
}
return 0;
}
/**
* Classifies the warning message: tries to guess a category from the warning message.
*
* @param message the message to check
* @return warning category, empty string if unknown
*/
protected String classifyWarning(final String message) {
if (StringUtils.contains(message, "proprietary")) {
return PROPRIETARY_API;
}
if (StringUtils.contains(message, "deprecated")) {
return DEPRECATION;
}
return StringUtils.EMPTY;
}
/**
* Returns a category for the current warning. If the provided category is
* not empty, then a capitalized string is returned. Otherwise the category
* is obtained from the specified message text.
* @param group
* the warning category (might be empty)
* @param message
* the warning message
*
* @return the actual category
*/
protected String classifyIfEmpty(final String group, final String message) {
String category = StringUtils.capitalize(group);
if (StringUtils.isEmpty(category)) {
category = classifyWarning(message);
}
return category;
}
}