/*
 * LensKit, an open source recommender systems toolkit.
 * Copyright 2010-2014 LensKit Contributors.  See CONTRIBUTORS.md.
 * Work on LensKit has been funded by the National Science Foundation under
 * grants IIS 05-34939, 08-08692, 08-12148, and 10-17697.
 *
 * 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 2.1 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 General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.lenskit;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.ref.SoftReference;
import java.util.Properties;
import java.util.Set;

/**
 * Get access to general information about LensKit.
 *
 * @since 2.1
 * @author <a href="http://www.grouplens.org">GroupLens Research</a>
 */
public final class LenskitInfo {
    private static final Logger logger = LoggerFactory.getLogger(LenskitInfo.class);
    private static SoftReference<Set<String>> revisionSet;

    private LenskitInfo() {
    }

    private static Set<String> loadRevisionSet() {
        ImmutableSet.Builder<String> revisions = ImmutableSet.builder();
        InputStream input = LenskitInfo.class.getResourceAsStream("/META-INF/lenskit/git-commits.lst");
        if (input != null) {
            try (Reader reader = new InputStreamReader(input, Charsets.UTF_8);
                 BufferedReader lines = new BufferedReader(reader)) {
                String line;
                while ((line = lines.readLine()) != null) {
                    revisions.add(StringUtils.trim(line));
                }
            } catch (IOException e) {
                throw new RuntimeException("error reading revision list", e);
            } finally {
                try {
                    input.close();
                } catch (IOException e) {
                    logger.error("error closing git-commit list", e);
                }
            }
        } else {
            logger.warn("cannot find LensKit revision list");
        }
        Set<String> revset = revisions.build();
        logger.debug("have {} active revisions", revset.size());
        return revset;
    }

    /**
     * Get the set of revisions LensKit is built from.  This is in the order returned by {@code git log},
     * so the head revision is first.
     * @return The set of revisions included in this build of LensKit.
     */
    public static synchronized Set<String> getRevisions() {
        Set<String> revisions = revisionSet == null ? null : revisionSet.get();
        if (revisions == null) {
            revisions = loadRevisionSet();
            revisionSet = new SoftReference<>(revisions);
        }
        return revisions;
    }

    /**
     * Query whether this version of LensKit includes a particular revision.
     *
     * @param revision The revision to query.
     * @return {@code true} if the LensKit source is descended from {@code revision}.
     */
    public static boolean includesRevision(String revision) {
        return getRevisions().contains(revision);
    }

    /**
     * Get the HEAD revision from which LensKit was built.
     * @return The revision from which this version of LensKit was built.
     */
    public static String getHeadRevision() {
        return getRevisions().iterator().next();
    }

    /**
     * Get the current LensKit version.
     * @return The LensKit version.
     */
    public static String lenskitVersion() {
        Properties props = new Properties();
        InputStream stream = LenskitInfo.class.getResourceAsStream("/META-INF/lenskit/version.properties");
        try {
            props.load(stream);
        } catch (IOException e) {
            throw new RuntimeException("properties error", e);
        } finally {
            try {
                stream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return props.getProperty("lenskit.version");
    }
}

@@ -1198,16 +1198,49 @@
 ctory;%0A%0A
+import javax.annotation.Nonnull;%0A
 import j
@@ -1285,16 +1285,43 @@
 erence;%0A
+import java.util.Iterator;%0A
 import j
@@ -2353,49 +2353,39 @@
     
-throw new RuntimeException(%22error
+logger.warn(%22Could not
  read
-ing
+ Git
  rev
@@ -3081,32 +3081,45 @@
 ensKit.%0A     */%0A
+    @Nonnull%0A
     public stati
@@ -3911,32 +3911,45 @@
  built.%0A     */%0A
+    @Nonnull%0A
     public stati
@@ -3977,38 +3977,55 @@
 ion() %7B%0A        
-return
+Iterator%3CString%3E iter =
  getRevisions().
@@ -4038,16 +4038,128 @@
 or()
-.next();
+;%0A        if (iter.hasNext()) %7B%0A            return iter.next();%0A        %7D else %7B%0A            return %22UNKNOWN%22;%0A        %7D
 %0A   
@@ -4246,32 +4246,45 @@
 ersion.%0A     */%0A
+    @Nonnull%0A
     public stati
@@ -4356,32 +4356,37 @@
 ties();%0A        
+try (
 InputStream stre
@@ -4467,29 +4467,17 @@
 erties%22)
-;%0A        try
+)
  %7B%0A     
@@ -4553,227 +4553,68 @@
     
-throw new RuntimeException(%22properties error%22, e);%0A        %7D finally %7B%0A            try %7B%0A                stream.close();%0A            %7D catch (IOException e) %7B%0A                throw new RuntimeException(e);%0A            %7D
+logger.warn(%22could not load LensKit version properties%22, e);
 %0A   
@@ -4670,16 +4670,27 @@
 version%22
+, %22UNKNOWN%22
 );%0A    %7D

Remove RuntimeException throws from LenskitInfo