package hudson.plugins.warnings;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.plugins.analysis.core.PluginDescriptor;
import hudson.plugins.warnings.parser.Warning;
import hudson.util.CopyOnWriteList;
import hudson.util.FormValidation;
import hudson.util.FormValidation.Kind;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
/**
* Descriptor for the class {@link WarningsPublisher}. Used as a singleton. The
* class is marked as public so that it can be accessed from views.
*
* @author Ulli Hafner
*/
@Extension(ordinal = 100) // NOCHECKSTYLE
public final class WarningsDescriptor extends PluginDescriptor {
/** Plug-in name. */
private static final String PLUGIN_NAME = "warnings";
/** Icon to use for the result and project action. */
private static final String ACTION_ICON = "/plugin/warnings/icons/warnings-24x24.png";
private static final int MAX_MESSAGE_LENGTH = 60;
private final CopyOnWriteList<GroovyParser> groovyParsers = new CopyOnWriteList<GroovyParser>();
/**
* Instantiates a new {@link WarningsDescriptor}.
*/
public WarningsDescriptor() {
super(WarningsPublisher.class);
}
/** {@inheritDoc} */
@Override
public String getDisplayName() {
return Messages.Warnings_Publisher_Name();
}
/** {@inheritDoc} */
@Override
public String getPluginName() {
return PLUGIN_NAME;
}
/** {@inheritDoc} */
@Override
public String getIconUrl() {
return ACTION_ICON;
}
/** {@inheritDoc} */
@SuppressWarnings("rawtypes")
@Override
public boolean isApplicable(final Class<? extends AbstractProject> jobType) {
return true;
}
/** {@inheritDoc} */
@Override
public WarningsPublisher newInstance(final StaplerRequest request, final JSONObject formData) throws FormException {
Set<String> parsers = extractParsers(formData);
WarningsPublisher publisher = request.bindJSON(WarningsPublisher.class, formData);
publisher.setParserNames(parsers);
return publisher;
}
/**
* Extract the list of parsers to use from the JSON form data.
*
* @param formData
* the JSON form data
* @return the list of parsers to use
*/
private Set<String> extractParsers(final JSONObject formData) {
Set<String> parsers = new HashSet<String>();
Object values = formData.get("parsers");
if (values instanceof JSONArray) {
JSONArray array = (JSONArray)values;
for (int i = 0; i < array.size(); i++) {
JSONObject element = array.getJSONObject(i);
parsers.add(element.getString("parserName"));
}
formData.remove("parsers");
}
else if (values instanceof JSONObject) {
JSONObject object = (JSONObject)values;
parsers.add(object.getString("parserName"));
formData.remove("parsers");
}
return parsers;
}
/**
* Returns the configured Groovy parsers.
*
* @return the Groovy parsers
*/
public CopyOnWriteList<GroovyParser> getParsers() {
return groovyParsers;
}
@Override
public boolean configure(final StaplerRequest req, final JSONObject formData) {
groovyParsers.replaceBy(req.bindJSONToList(GroovyParser.class, formData.get("parsers")));
return true;
}
/**
* Performs on-the-fly validation on the name of the parser that needs to be unique.
*
* @param name
* the name of the parser
* @return the validation result
*/
public FormValidation doCheckName(@QueryParameter(required = true) final String name) {
return GroovyParser.doCheckName(name);
}
/**
* Performs on-the-fly validation on the regular expression.
*
* @param regexp
* the regular expression
* @return the validation result
*/
public FormValidation doCheckRegexp(@QueryParameter(required = true) final String regexp) {
return GroovyParser.doCheckRegexp(regexp);
}
/**
* Performs on-the-fly validation on the Groovy script.
*
* @param script
* the script
* @return the validation result
*/
public FormValidation doCheckScript(@QueryParameter(required = true) final String script) {
return GroovyParser.doCheckScript(script);
}
/**
* Parses the example message with the specified regular expression and script.
*
* @param example
* example that should be resolve to a warning
* @param regexp
* the regular expression
* @param script
* the script
* @return the validation result
*/
public FormValidation doCheckExample(@QueryParameter final String example,
@QueryParameter final String regexp, @QueryParameter final String script) {
if (StringUtils.isNotBlank(example) && StringUtils.isNotBlank(regexp) && StringUtils.isNotBlank(script)) {
return parseExample(script, example, regexp);
}
else {
return FormValidation.ok();
}
}
/**
* Parses the example and returns a validation result of type
* {@link Kind#OK} if a warning has been found.
*
* @param script
* the script that parses the expression
* @param example
* example text that will be matched by the regular expression
* @param regexp
* the regular expression
* @return a result of {@link Kind#OK} if a warning has been found
*/
private FormValidation parseExample(final String script, final String example, final String regexp) {
Pattern pattern = Pattern.compile(regexp);
Matcher matcher = pattern.matcher(example);
if (matcher.matches()) {
Binding binding = new Binding();
binding.setVariable("matcher", matcher);
GroovyShell shell = new GroovyShell(WarningsDescriptor.class.getClassLoader(), binding);
Object result = null;
try {
result = shell.evaluate(script);
}
catch (Exception exception) { // NOCHECKSTYLE: catch all exceptions of the Groovy script
return FormValidation.error(
Messages.Warnings_GroovyParser_Error_Example_exception(exception.getMessage()));
}
if (result instanceof Warning) {
StringBuilder okMessage = new StringBuilder(Messages.Warnings_GroovyParser_Error_Example_ok_title());
Warning warning = (Warning)result;
message(okMessage, Messages.Warnings_GroovyParser_Error_Example_ok_file(warning.getFileName()));
message(okMessage, Messages.Warnings_GroovyParser_Error_Example_ok_line(warning.getPrimaryLineNumber()));
message(okMessage, Messages.Warnings_GroovyParser_Error_Example_ok_priority(warning.getPriority().getLongLocalizedString()));
message(okMessage, Messages.Warnings_GroovyParser_Error_Example_ok_category(warning.getCategory()));
message(okMessage, Messages.Warnings_GroovyParser_Error_Example_ok_type(warning.getType()));
message(okMessage, Messages.Warnings_GroovyParser_Error_Example_ok_message(warning.getMessage()));
return FormValidation.ok(okMessage.toString());
}
else {
return FormValidation.error(Messages.Warnings_GroovyParser_Error_Example_wrongReturnType(result));
}
}
else {
return FormValidation.error(Messages.Warnings_GroovyParser_Error_Example_regexpDoesNotMatch());
}
}
private void message(final StringBuilder okMessage, final String message) {
okMessage.append("\n");
int max = MAX_MESSAGE_LENGTH;
if (message.length() > max) {
int size = max / 2 - 1;
okMessage.append(message.substring(0, size));
okMessage.append("[...]");
okMessage.append(message.substring(message.length() - size, message.length()));
}
else {
okMessage.append(message);
}
}
}