de.uni_freiburg.informatik.ultimate.licence_manager.authors.SvnAuthorProvider.java Source code

Java tutorial

Introduction

Here is the source code for de.uni_freiburg.informatik.ultimate.licence_manager.authors.SvnAuthorProvider.java

Source

/*
 * Copyright (C) 2015 Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
 * Copyright (C) 2015 University of Freiburg
 * 
 * This file is part of the ULTIMATE licence-manager.
 * 
 * The ULTIMATE licence-manager 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.
 * 
 * The ULTIMATE licence-manager 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 the ULTIMATE licence-manager. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Additional permission under GNU GPL version 3 section 7:
 * If you modify the ULTIMATE licence-manager, or any covered work, by linking
 * or combining it with Eclipse RCP (or a modified version of Eclipse RCP), 
 * containing parts covered by the terms of the Eclipse Public License, the 
 * licensors of the ULTIMATE licence-manager grant you additional permission 
 * to convey the resulting work.
 */
package de.uni_freiburg.informatik.ultimate.licence_manager.authors;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.io.IOUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import de.uni_freiburg.informatik.ultimate.licence_manager.LicencedFile;
import de.uni_freiburg.informatik.ultimate.licence_manager.Main;
import de.uni_freiburg.informatik.ultimate.licence_manager.filetypes.FileType;
import de.uni_freiburg.informatik.ultimate.licence_manager.filetypes.IFileTypeDependentOperation;
import de.uni_freiburg.informatik.ultimate.licence_manager.util.DateUtils;
import de.uni_freiburg.informatik.ultimate.licence_manager.util.ProcessUtils;
import de.uni_freiburg.informatik.ultimate.licence_manager.util.ProcessUtils.StreamGobbler;

/**
 * 
 * @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
 *
 */
public class SvnAuthorProvider implements IAuthorProvider {

    private static final boolean SVN_AVAILABLE = isSvnAvailable();
    private static final float THRESHOLD_PERCENTAGE = 10;
    private static String sSvnVersion;

    @Override
    public List<Author> getAuthors(LicencedFile file, IFileTypeDependentOperation operation) {
        return getAuthors(file.getFile());
    }

    @Override
    public boolean isUsable(LicencedFile file, FileType fileType) {
        return SVN_AVAILABLE && !Main.hasOption(Main.OPTION_DISABLE_SVN) && fileType != FileType.UNKNOWN
                && isUnderVersionControl(file.getFile().getAbsolutePath());
    }

    public String getSvnVersion() {
        return sSvnVersion;
    }

    private static boolean isSvnAvailable() {
        final ProcessBuilder pbuilder = new ProcessBuilder("svn", "--version");
        try {
            final Process process = pbuilder.start();
            if (process.waitFor(1000, TimeUnit.MILLISECONDS)) {
                final List<String> lines = IOUtils.readLines(process.getInputStream(), Charset.defaultCharset());
                if (lines == null || lines.isEmpty()) {
                    return false;
                }
                sSvnVersion = lines.get(0);
                return true;
            }
        } catch (IOException | InterruptedException e) {
            System.err.println("Could not find 'svn' executable, disabling author provider");
            return false;
        }
        return false;
    }

    private boolean isUnderVersionControl(String absolutePath) {
        final ProcessBuilder pbuilder = new ProcessBuilder("svn", "info", absolutePath);
        try {
            final Process process = pbuilder.start();
            if (process.waitFor(1000, TimeUnit.MILLISECONDS)) {
                final List<String> lines = IOUtils.readLines(process.getInputStream(), Charset.defaultCharset());
                if (lines == null || lines.isEmpty()) {
                    return false;
                }
                return !lines.get(0).contains("is not a working copy");
            }
        } catch (IOException | InterruptedException e) {
            System.err.println("Could not find 'svn' executable, disabling author provider");
            return false;
        }
        return false;

    }

    private List<Author> getAuthors(File file) {
        final List<Author> rtr = new ArrayList<Author>();
        try {
            final InputStream output = getSvnBlameProcess(file.getAbsolutePath());
            final SvnBlameSAXHandler handler = parseSvnBlameOutput(output);
            for (final Entry<String, YearAndLineCount> entry : handler.mLinesPerAuthor.entrySet()) {
                final float percentage = (float) entry.getValue().LineCount / (float) handler.mMaxLineNumber * 100;
                if (percentage > THRESHOLD_PERCENTAGE) {
                    rtr.add(new Author(entry.getKey(), entry.getValue().Year, null));
                }
            }

        } catch (IOException | InterruptedException | ParserConfigurationException | SAXException e) {
            System.err.println("Error while executing 'svn blame': " + e.getMessage());
        }
        return rtr;
    }

    private InputStream getSvnBlameProcess(String absolutePath) throws IOException, InterruptedException {
        final Process process = new ProcessBuilder("svn", "blame", "--xml", absolutePath).start();
        ProcessUtils.inheritIO(process.getErrorStream(), System.err);
        StreamGobbler gobbler = ProcessUtils.attachGobbler(process.getInputStream());
        while (!process.waitFor(2500, TimeUnit.MILLISECONDS)) {
            System.out.print(".");
        }
        return gobbler.getFreshStream();
    }

    private SvnBlameSAXHandler parseSvnBlameOutput(InputStream input)
            throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser saxParser = factory.newSAXParser();
        SvnBlameSAXHandler handler = new SvnBlameSAXHandler();
        saxParser.parse(input, handler);
        return handler;
    }

    private static class SvnBlameSAXHandler extends DefaultHandler {

        private int mMaxLineNumber;
        private Map<String, YearAndLineCount> mLinesPerAuthor;

        private boolean mTagAuthor;
        private boolean mTagDate;
        private String mCurrentAuthor;

        private SvnBlameSAXHandler() {
            mLinesPerAuthor = new HashMap<String, YearAndLineCount>();
            mMaxLineNumber = 0;
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
                throws SAXException {
            super.startElement(uri, localName, qName, attributes);

            if (qName.equals("author")) {
                mTagAuthor = true;
                mMaxLineNumber++;
            }

            if (qName.equals("date")) {
                mTagDate = true;
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            super.characters(ch, start, length);

            if (mTagAuthor) {
                mCurrentAuthor = new String(ch, start, length);
                YearAndLineCount lineCount = getYearAndLineCount(mCurrentAuthor);
                lineCount.LineCount++;
                mTagAuthor = false;
                return;
            }

            if (mTagDate) {
                final String date = new String(ch, start, length);
                YearAndLineCount lineCount = getYearAndLineCount(mCurrentAuthor);
                try {
                    // <date>2013-10-23T08:21:21.894117Z</date>
                    lineCount.Year = DateUtils.min(DateUtils.getYear(date, "yyyy-MM-dd'T'HH:mm:ss.SSSSSSX"),
                            lineCount.Year);
                } catch (ParseException e) {
                    System.err.print(e.getMessage());
                }
                mTagDate = false;
                return;
            }
        }

        private YearAndLineCount getYearAndLineCount(final String authorName) {
            YearAndLineCount lineCount = mLinesPerAuthor.get(authorName);
            if (lineCount == null) {
                lineCount = new YearAndLineCount(DateUtils.getCurrentYear(), 0);
                mLinesPerAuthor.put(authorName, lineCount);
            }
            return lineCount;
        }
    }

    private static class YearAndLineCount {
        private String Year;
        private int LineCount;

        private YearAndLineCount(String year, int linecount) {
            Year = year;
            LineCount = linecount;
        }
    }

}