com.stevpet.sonar.plugins.dotnet.mscover.parser.XmlParserSubject.java Source code

Java tutorial

Introduction

Here is the source code for com.stevpet.sonar.plugins.dotnet.mscover.parser.XmlParserSubject.java

Source

/*******************************************************************************
 *
 * SonarQube MsCover Plugin
 * Copyright (C) 2015 SonarSource
 * dev@sonar.codehaus.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 *
 * Author: Peter Stevens, peter@famstevens.eu
 *******************************************************************************/
package com.stevpet.sonar.plugins.dotnet.mscover.parser;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.Location;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;

import org.apache.commons.lang.StringUtils;
import org.codehaus.staxmate.SMInputFactory;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.utils.SonarException;

import com.stevpet.sonar.plugins.dotnet.mscover.parser.annotations.AttributeMatcher;
import com.stevpet.sonar.plugins.dotnet.mscover.parser.annotations.ElementMatcher;
import com.stevpet.sonar.plugins.dotnet.mscover.parser.annotations.PathMatcher;
import com.stevpet.sonar.plugins.dotnet.mscover.parser.exceptions.MsCoverParserException;
import com.stevpet.sonar.plugins.dotnet.mscover.parser.exceptions.ParserSubjectErrorException;
import com.stevpet.sonar.plugins.dotnet.mscover.parser.interfaces.ParserObserver;
import com.stevpet.sonar.plugins.dotnet.mscover.parser.interfaces.ParserSubject;

public abstract class XmlParserSubject implements ParserSubject {

    private static final Logger LOG = LoggerFactory.getLogger(XmlParserSubject.class);
    private List<ParserObserver> observers = new ArrayList<ParserObserver>();

    List<String> parentElements = new ArrayList<String>();
    private int line;
    private int column;
    private ParserData parserData = new ParserData();

    public XmlParserSubject() {
        String[] names = getHierarchy();
        for (String name : names) {
            parentElements.add(name);
        }
    }

    public List<ParserObserver> getObservers() {
        return observers;
    }

    public abstract String[] getHierarchy();

    public void parseString(String string) {
        SMInputCursor cursor;
        try {
            cursor = getCursorFromString(string);
            parse(cursor);
        } catch (FactoryConfigurationError e) {
            LOG.error("FactoryConfigurationError", e);
            throw new SonarException(e);
        } catch (XMLStreamException e) {
            String msg = "XMLStreamException in string";
            LOG.error(msg, e);
            throw new SonarException(msg, e);
        }
    }

    /**
     * Gets the cursor for the given file
     * 
     * @param file
     * @return
     * @throws FactoryConfigurationError
     * @throws XMLStreamException
     */
    public SMInputCursor getCursorFromString(String string) {
        SMInputCursor result = null;
        try {
            SMInputFactory inf = new SMInputFactory(XMLInputFactory.newInstance());
            InputStream inputStream = new ByteArrayInputStream(string.getBytes());
            SMHierarchicCursor cursor = inf.rootElementCursor(inputStream);
            result = cursor.advance();
        } catch (XMLStreamException e) {
            String msg = "Could not create cursor " + e.getMessage();
            LOG.error(msg);
            throw new SonarException(msg, e);
        }
        return result;
    }

    public void parseFile(File file) {
        SMInputCursor cursor;
        try {
            cursor = getCursor(file);
            parse(cursor);
        } catch (FactoryConfigurationError e) {
            LOG.error("FactoryConfigurationError", e);
            throw new SonarException(e);
        } catch (XMLStreamException e) {
            String msg = "XMLStreamException in " + file.getAbsolutePath() + " column/line " + column + "/" + line;
            LOG.error(msg, e);
            throw new SonarException(msg, e);
        }
        checkOnErrors(file);

    }

    private void checkOnErrors(File file) {
        for (ParserObserver observer : observers) {
            if (observer.hasError()) {
                throw new ParserSubjectErrorException(file);
            }
        }
    }

    private void parse(SMInputCursor rootCursor) throws XMLStreamException {
        injectVariablesInObservers();
        SMInputCursor childCursor = rootCursor.childElementCursor();
        parseChild("", childCursor);
    }

    private void injectVariablesInObservers() {
        for (ParserObserver observer : observers) {
            observer.injectParserData(parserData);
        }
    }

    public void registerObserver(ParserObserver observer) {
        observers.add(observer);
    }

    private boolean parseChild(String path, SMInputCursor childCursor) throws XMLStreamException {
        boolean parsedChild = false;
        parserData.levelDown();
        while ((childCursor.getNext()) != null) {
            if (!parserData.parseLevelAndBelow()) {
                processStartElement(path, childCursor);
                parsedChild = true;
            }
        }
        parserData.levelUp();
        return parsedChild;
    }

    private void processStartElement(String path, SMInputCursor childCursor) throws XMLStreamException {
        String name = childCursor.getLocalName();
        if ("schema".equals(name)) {
            return;
        }

        String elementPath = createElementPath(path, name);
        processAttributes(elementPath, name, childCursor);
        processElement(elementPath, name, childCursor);
    }

    private void processElement(String elementPath, String name, SMInputCursor childCursor)
            throws XMLStreamException {

        if (parentElements.contains(name)) {
            parseChild(elementPath, childCursor.childElementCursor());
        } else {
            updateLocation(childCursor);
            String text = getTrimmedElementStringValue(childCursor);
            invokeElementObservers(elementPath, name, text);
        }
    }

    private void invokeElementObservers(String path, String name, String text) {
        for (ParserObserver observer : observers) {
            if (observer.isMatch(path)) {
                observer.observeElement(name, text);
                invokeAnnotatedElementMethods(path, name, text, observer);
            }
        }
    }

    private void processAttributes(String path, String name, SMInputCursor elementCursor)
            throws XMLStreamException {
        int attributeCount = elementCursor.getAttrCount();
        for (int index = 0; index < attributeCount; index++) {
            String attributeValue = elementCursor.getAttrValue(index);
            String attributeName = elementCursor.getAttrLocalName(index);
            updateLocation(elementCursor);
            invokeAttributeObservers(name, path, attributeValue, attributeName);
        }
    }

    private void updateLocation(SMInputCursor elementCursor) {
        Location location;
        try {
            location = elementCursor.getCursorLocation();

        } catch (XMLStreamException e) {
            throw new MsCoverParserException("Exception thrown on getting location", e);
        }
        line = location.getLineNumber();
        column = location.getColumnNumber();

    }

    private void invokeAttributeObservers(String elementName, String path, String attributeValue,
            String attributeName) {
        for (ParserObserver observer : observers) {
            if (observer.isMatch(path)) {
                observer.observeAttribute(elementName, path, attributeValue, attributeName);
                invokeAnnotatedMethods(elementName, attributeValue, attributeName, observer);
            }
        }
    }

    private void invokeAnnotatedElementMethods(String elementPath, String elementName, String elementValue,
            ParserObserver observer) {
        Method[] methods = observer.getClass().getMethods();
        for (Method method : methods) {
            invokeAnnotatedElementMethod(elementName, elementValue, observer, method);
            invokePathMatcherMethod(elementPath, elementValue, observer, method);
        }
    }

    private void invokePathMatcherMethod(String path, String elementValue, ParserObserver observer, Method method) {
        PathMatcher annos = method.getAnnotation(PathMatcher.class);
        if (annos == null) {
            return;
        }
        if (path.equals(annos.path())) {
            invokeMethod(elementValue, observer, method);
        }

    }

    private void invokeAnnotatedElementMethod(String elementName, String elementValue, ParserObserver observer,
            Method method) {
        ElementMatcher annos = method.getAnnotation(ElementMatcher.class);

        if (annos == null) {
            return;
        }
        if (elementName.equals(annos.elementName())) {
            invokeMethod(elementValue, observer, method);
        }
    }

    private void invokeMethod(String elementValue, ParserObserver observer, Method method) {
        try {
            method.invoke(observer, elementValue);
        } catch (InvocationTargetException e) {
            String msg = "Invocation Target Exception thrown when invoking method " + observer.getClass().getName()
                    + ":" + method.getName() + lineMsg();
            LOG.error(msg, e);
            throw new SonarException(msg, e);
        } catch (IllegalAccessException e) {
            String msg = "Illegal Access Exception thrown when invoking method " + observer.getClass().getName()
                    + ":" + method.getName() + lineMsg();
            LOG.error(msg, e);
            throw new SonarException(msg, e);
        } catch (IllegalArgumentException e) {
            String msg = "Illegal Argument Exception thrown when invoking method " + observer.getClass().getName()
                    + ":" + method.getName() + lineMsg();
            LOG.error(msg, e);
            throw new SonarException(msg, e);
        }
    }

    private String lineMsg() {
        return " line/column = " + line + "/" + column;
    }

    private void invokeAnnotatedMethods(String elementName, String attributeValue, String attributeName,
            ParserObserver observer) {
        Method[] methods = observer.getClass().getMethods();
        for (Method method : methods) {
            invokeAnnotatedMethod(elementName, attributeValue, attributeName, observer, method);
        }
    }

    private void invokeAnnotatedMethod(String elementName, String attributeValue, String attributeName,
            ParserObserver observer, Method method) {
        AttributeMatcher annos = method.getAnnotation(AttributeMatcher.class);

        if (annos == null) {
            return;
        }
        if (elementName.equals(annos.elementName()) && attributeName.equals(annos.attributeName())) {
            invokeMethod(attributeValue, observer, method);
        }
    }

    private String getTrimmedElementStringValue(SMInputCursor childCursor) throws XMLStreamException {
        String text = childCursor.getElemStringValue();
        if (StringUtils.isNotEmpty(text)) {
            text = text.trim();
        }
        return text;
    }

    private String createElementPath(String path, String name) {
        String elementPath;
        if ("".equals(path)) {
            elementPath = name;
        } else {
            elementPath = path + "/" + name;
        }
        return elementPath;
    }

    /**
     * Gets the cursor for the given file
     * 
     * @param file
     * @return
     * @throws FactoryConfigurationError
     * @throws XMLStreamException
     */
    public SMInputCursor getCursor(File file) {
        SMInputCursor result = null;
        try {
            SMInputFactory inf = new SMInputFactory(XMLInputFactory.newInstance());
            SMHierarchicCursor cursor = inf.rootElementCursor(file);
            result = cursor.advance();
        } catch (XMLStreamException e) {
            String msg = "Could not create cursor " + e.getMessage();
            LOG.error(msg);
            throw new SonarException(msg, e);
        }
        return result;
    }

}