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;
    }
}

@@ -70,20 +70,16 @@
 is.util.
-Java
 PackageD
@@ -85,16 +85,17 @@
 Detector
+s
 ;%0Aimport
@@ -3207,37 +3207,31 @@
     
-String packageName = new Java
+warning.setPackageName(
 Pack
@@ -3241,18 +3241,17 @@
 Detector
-()
+s
 .detectP
@@ -3287,56 +3287,8 @@
 e())
-;%0A            warning.setPackageName(packageName
 );%0A 

Set path name after each scan to provide package support for !java.