Package hudson.plugins.cobertura

Source Code of hudson.plugins.cobertura.CoberturaXmlHandler

package hudson.plugins.cobertura;

import hudson.plugins.cobertura.targets.CoverageElement;
import hudson.plugins.cobertura.targets.CoverageMetric;
import hudson.plugins.cobertura.targets.CoverageResult;
import hudson.util.IOException2;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Created by IntelliJ IDEA.
*
* @author connollys
* @since 03-Jul-2007 09:03:30
*/
public class CoberturaCoverageParser {

    /**
     * Do not instantiate CoberturaCoverageParser.
     */
    private CoberturaCoverageParser() {
    }

    public static CoverageResult parse(File inFile, CoverageResult cumulative) throws IOException {
        return parse(inFile, cumulative, null);
    }

    public static CoverageResult parse(File inFile, CoverageResult cumulative, Set<String> sourcePaths) throws IOException {
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        try {
            fileInputStream = new FileInputStream(inFile);
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            return parse(bufferedInputStream, cumulative, sourcePaths);
        } finally {
            IOUtils.closeQuietly(bufferedInputStream);
            IOUtils.closeQuietly(fileInputStream);
        }
    }

    public static CoverageResult parse(InputStream in, CoverageResult cumulative) throws IOException {
        return parse(in, cumulative, null);
    }

    public static CoverageResult parse(InputStream in, CoverageResult cumulative, Set<String> sourcePaths) throws IOException {
        if (in == null) throw new NullPointerException();
        SAXParserFactory factory = SAXParserFactory.newInstance();
        factory.setValidating(false);
        try {
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        } catch (ParserConfigurationException e) {
        } catch (SAXNotRecognizedException e) {
        } catch (SAXNotSupportedException e) {
        }
        try {
            SAXParser parser = factory.newSAXParser();
            CoberturaXmlHandler handler = new CoberturaXmlHandler(cumulative);
            parser.parse(in, handler);
            if (sourcePaths != null) {
                sourcePaths.addAll(handler.getSourcePaths());
            }
            return handler.getRootCoverage();
        } catch (ParserConfigurationException e) {
            throw new IOException2("Cannot parse coverage results", e);
        } catch (SAXException e) {
            throw new IOException2("Cannot parse coverage results", e);
        }
    }
}

/**
* Parses coverage XML data.
*/
class CoberturaXmlHandler extends DefaultHandler {
    private static final String DEFAULT_PACKAGE = "<default>";
    // patterns static for performance ("Instances of Pattern are immutable and are safe for use by multiple concurrent threads" according to javadoc)
    private static final Pattern CONDITION_COVERAGE_PATTERN = Pattern.compile("(\\d*)\\s*\\%\\s*\\((\\d*)/(\\d*)\\)");
    private static final Pattern METHOD_SIGNATURE_PATTERN = Pattern.compile("\\((.*)\\)(.*)");
    private static final Pattern METHOD_ARGS_PATTERN = Pattern.compile("\\[*([TL][^\\;]*\\;)|([ZCBSIFJDV])");
    private CoverageResult rootCoverage;
    private Stack<CoverageResult> stack = new Stack<CoverageResult>();
    private Set<String> sourcePaths = new HashSet<String>();
    private boolean inSources = false;
    private boolean inSource = false;
    private StringBuilder sourceDir = new StringBuilder();

    public CoberturaXmlHandler(CoverageResult rootCoverage) {
        this.rootCoverage = rootCoverage;
    }

    /**
     * {@inheritDoc}
     */
    public void startDocument() throws SAXException {
        super.startDocument();
        if (this.rootCoverage == null) {
            this.rootCoverage = new CoverageResult(CoverageElement.PROJECT, null, Messages.CoberturaCoverageParser_name());
        }
        stack.clear();
        inSource = false;
        inSources = false;
    }

    /**
     * {@inheritDoc}
     */
    public void endDocument() throws SAXException {
        if (!stack.empty() || inSource || inSources) {
            throw new SAXException("Unbalanced parse of cobertura coverage results.");
        }
        super.endDocument();    //To change body of overridden methods use File | Settings | File Templates.
    }

    private void descend(CoverageElement childType, String childName) {
        CoverageResult child = rootCoverage.getChild(childName);
        stack.push(rootCoverage);
        if (child == null) {
            rootCoverage = new CoverageResult(childType, rootCoverage, childName);
        } else {
            rootCoverage = child;
        }
    }

    private void ascend(CoverageElement element) {
        while (rootCoverage != null && rootCoverage.getElement() != element) {
            rootCoverage = stack.pop();
        }
        if (rootCoverage != null) {
            rootCoverage = stack.pop();
        }
    }

    /**
     * {@inheritDoc}
     */
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        String name = attributes.getValue("name");
        if ("sources".equals(qName)) {
            inSources = true;
        } else if ("source".equals(qName)) {
            sourceDir = new StringBuilder();
            inSource = true;
        } else if ("coverage".equals(qName)) {
        } else if ("package".equals(qName)) {
            if ("".equals(name) || null == name) {
                name = DEFAULT_PACKAGE;
            }
            descend(CoverageElement.JAVA_PACKAGE, name);
        } else if ("class".equals(qName)) {
            assert rootCoverage.getElement() == CoverageElement.JAVA_PACKAGE;
            // cobertura combines file and class
            String filename = attributes.getValue("filename").replace('\\', '/');
            // filename should be a relative path.
            // See https://issues.jenkins-ci.org/browse/JENKINS-16252
            if (filename.startsWith("\\") || filename.startsWith("/")) {
                filename = filename.substring(1);
            }
            String relativeFilename = filename;

            final String packageName = rootCoverage.getName();
            final String packagePath = packageName.replace('.', '/') + "/";
            if (!DEFAULT_PACKAGE.equals(packageName)) {
                if (relativeFilename.startsWith(packagePath)) {
                    relativeFilename = filename.substring(packagePath.length());
                }
            }
            if (name.startsWith(packageName + ".")) {
                name = name.substring(packageName.length() + 1);
            }
            descend(CoverageElement.JAVA_FILE, relativeFilename);
            rootCoverage.setRelativeSourcePath(filename);
            descend(CoverageElement.JAVA_CLASS, name);
        } else if ("method".equals(qName)) {
            String methodName = buildMethodName(name, attributes.getValue("signature"));
            descend(CoverageElement.JAVA_METHOD, methodName);
        } else if ("line".equals(qName)) {
            String hitsString = attributes.getValue("hits");
            String lineNumber = attributes.getValue("number");
            int denominator = 0;
            int numerator = 0;
            if (Boolean.parseBoolean(attributes.getValue("branch"))) {
                final String conditionCoverage = attributes.getValue("condition-coverage");
                if (conditionCoverage != null) {
                    // some cases in the wild have branch = true but no condition-coverage attribute

                    // should be of the format xxx% (yyy/zzz),
                    // or xxx % (yyy/zzz) for French,
                    // because cobertura uses the default locale as said in
                    // http://sourceforge.net/tracker/?func=detail&aid=3296149&group_id=130558&atid=720015
                    Matcher matcher = CONDITION_COVERAGE_PATTERN.matcher(conditionCoverage);
                    if (matcher.matches()) {
                        assert matcher.groupCount() == 3;
                        final String numeratorStr = matcher.group(2);
                        final String denominatorStr = matcher.group(3);
                        try {
                            numerator = Integer.parseInt(numeratorStr);
                            denominator = Integer.parseInt(denominatorStr);
                            rootCoverage.updateMetric(CoverageMetric.CONDITIONAL, Ratio.create(numerator, denominator));
                        } catch (NumberFormatException e) {
                            // ignore
                        }
                    }
                }
            }
            try {
                int hits = Integer.parseInt(hitsString);
                int number = Integer.parseInt(lineNumber);
                if (denominator == 0) {
                    rootCoverage.paint(number, hits);
                } else {
                    rootCoverage.paint(number, hits, numerator, denominator);
                }
                rootCoverage.updateMetric(CoverageMetric.LINE, Ratio.create((hits == 0) ? 0 : 1, 1));
            } catch (NumberFormatException e) {
                // ignore
            }
        }

    }

    private String buildMethodName(String name, String signature) {
        Matcher signatureMatcher = METHOD_SIGNATURE_PATTERN.matcher(signature);
        StringBuilder methodName = new StringBuilder();
        if (signatureMatcher.matches()) {
            String returnType = signatureMatcher.group(2);
            Matcher matcher = METHOD_ARGS_PATTERN.matcher(returnType);
            if (matcher.matches()) {
                methodName.append(parseMethodArg(matcher.group()));
                methodName.append(' ');
            }
            methodName.append(name);
            String args = signatureMatcher.group(1);
            matcher = METHOD_ARGS_PATTERN.matcher(args);
            methodName.append('(');
            boolean first = true;
            while (matcher.find()) {
                if (!first) {
                    methodName.append(',');
                }
                methodName.append(parseMethodArg(matcher.group()));
                first = false;
            }
            methodName.append(')');
        } else {
            methodName.append(name);
        }
        return methodName.toString();
    }

    private String parseMethodArg(String s) {
        char c = s.charAt(0);
        int end;
        switch (c) {
            case'Z':
                return "boolean";
            case'C':
                return "char";
            case'B':
                return "byte";
            case'S':
                return "short";
            case'I':
                return "int";
            case'F':
                return "float";
            case'J':
                return "";
            case'D':
                return "double";
            case'V':
                return "void";
            case'[':
                return parseMethodArg(s.substring(1)) + "[]";
            case'T':
            case'L':
                end = s.indexOf(';');
                return s.substring(1, end).replace('/', '.');
        }
        return s;
    }

    /**
     * {@inheritDoc}
     */
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("sources".equals(qName)) {
            inSources = false;
        } else if ("source".equals(qName)) {
            if (inSources && inSource) {
                sourcePaths.add(sourceDir.toString().trim());
                }
            inSource = false;
        } else if ("coverage".equals(qName)) {
        } else if ("package".equals(qName)) {
            ascend(CoverageElement.JAVA_PACKAGE);
        } else if ("class".equals(qName)) {
            ascend(CoverageElement.JAVA_CLASS);
            ascend(CoverageElement.JAVA_FILE);
        } else if ("method".equals(qName)) {
            ascend(CoverageElement.JAVA_METHOD);
        }
        super.endElement(uri, localName, qName);    //To change body of overridden methods use File | Settings | File Templates.
    }

    /**
     * {@inheritDoc}
     */
    public void characters(char[] ch, int start, int length) throws SAXException {
        sourceDir.append(new String(ch, start, length));
    }

    /**
     * Getter for property 'rootCoverage'.
     *
     * @return Value for property 'rootCoverage'.
     */
    public CoverageResult getRootCoverage() {
        return rootCoverage;
    }

    /**
     * Getter for property 'sourcePaths'.
     *
     * @return Value for property 'sourcePaths'.
     */
    public Set<String> getSourcePaths() {
        return Collections.unmodifiableSet(sourcePaths);
    }

}
TOP

Related Classes of hudson.plugins.cobertura.CoberturaXmlHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.