From 47f1a90fa5049f3f23352641cc4f03297d8c0142 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 25 Oct 2017 10:31:52 -0700 Subject: [PATCH] FsfLicenseDataParser: Fill in implementation Pull the index (mapping from SPDX IDs to FSF IDs) during initialization, and then lookup each license as needed with a separate request. An implementation that hits isSpdxLicenseFsfLibre multiple times (or which wanted to extract multiple values for a single license) would be more efficient if we cached the per-license FSF response. I've left that off for now because our only consumer is just asking for libre-ness, and only doing that once per license. I've used a Boolean for the isSpdxLicenseFsfLibre to get a nullable value, so we can represent: * "yes, the FSF marks that license 'libre'" (true), * "no, the FSF considers that license non-free" (false), and * "we don't know the FSF opinion for that license" (null). --- src/org/spdx/tools/LicenseRDFAGenerator.java | 11 ++- .../FsfLicenseDataParser.java | 81 ++++++++++++++++--- 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/org/spdx/tools/LicenseRDFAGenerator.java b/src/org/spdx/tools/LicenseRDFAGenerator.java index fe63a190..498598a3 100644 --- a/src/org/spdx/tools/LicenseRDFAGenerator.java +++ b/src/org/spdx/tools/LicenseRDFAGenerator.java @@ -473,7 +473,16 @@ private static void writeLicenseList(String version, String releaseDate, * @param license */ private static void addExternalMetaData(SpdxListedLicense license) { - license.setFsfLibre(FsfLicenseDataParser.getFsfLicenseDataParser().isSpdxLicenseFsfLibre(license.getLicenseId())); + Boolean libre = null; + try { + libre = FsfLicenseDataParser.getFsfLicenseDataParser().isSpdxLicenseFsfLibre(license.getLicenseId()); + } catch (LicenseGeneratorException e) { + System.out.println(e.getMessage()); + libre = null; + } + if (libre != null) { + license.setFsfLibre(libre); + } } /** diff --git a/src/org/spdx/tools/licensegenerator/FsfLicenseDataParser.java b/src/org/spdx/tools/licensegenerator/FsfLicenseDataParser.java index 6b03a1f1..1aee2a32 100644 --- a/src/org/spdx/tools/licensegenerator/FsfLicenseDataParser.java +++ b/src/org/spdx/tools/licensegenerator/FsfLicenseDataParser.java @@ -15,6 +15,23 @@ */ package org.spdx.tools.licensegenerator; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.json.simple.parser.ParseException; +import org.spdx.tools.LicenseGeneratorException; + /** * Singleton class which returns information maintained by the Free Software Foundation * @@ -25,14 +42,40 @@ * */ public class FsfLicenseDataParser { - + private static FsfLicenseDataParser fsfLicenseDataParser = null; - - private FsfLicenseDataParser() { - //TODO: Initialize any cached data + private static Map fsfID; + + private FsfLicenseDataParser() throws LicenseGeneratorException { + fsfID = new HashMap(); + URL url; + try { + url = new URL("https://wking.github.io/fsf-api/licenses.json"); + } catch (MalformedURLException e) { + throw new LicenseGeneratorException("invalid FSF license-list URL", e); + } + try { + Reader reader = new BufferedReader(new InputStreamReader(url.openStream())); + JSONObject index = (JSONObject)JSONValue.parseWithException(reader); + Iterator iterator = index.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = (Entry)iterator.next(); + JSONObject value = (JSONObject)entry.getValue(); + JSONObject identifiers = (JSONObject)value.get("identifiers"); + if (identifiers == null) { + continue; + } + String spdx = (String)identifiers.get("spdx"); + if (spdx != null) { + fsfID.put(spdx, (String)entry.getKey()); + } + } + } catch (IOException|ParseException e) { + throw new LicenseGeneratorException("failure processing FSF license-list", e); + } } - - public static synchronized FsfLicenseDataParser getFsfLicenseDataParser() { + + public static synchronized FsfLicenseDataParser getFsfLicenseDataParser() throws LicenseGeneratorException { if (fsfLicenseDataParser == null) { fsfLicenseDataParser = new FsfLicenseDataParser(); } @@ -44,9 +87,29 @@ public static synchronized FsfLicenseDataParser getFsfLicenseDataParser() { * @param spdxLicenseId * @return */ - public boolean isSpdxLicenseFsfLibre(String spdxLicenseId) { - // TODO Implement - return false; + public Boolean isSpdxLicenseFsfLibre(String spdxLicenseId) throws LicenseGeneratorException { + String id = fsfID.get(spdxLicenseId); + if (id == null) { + return null; + } + URL url; + try { + url = new URL(String.format("https://wking.github.io/fsf-api/%s.json", id)); + } catch (MalformedURLException e) { + throw new LicenseGeneratorException("invalid FSF license-list URL", e); + } + JSONArray tags; + try { + Reader reader = new BufferedReader(new InputStreamReader(url.openStream())); + JSONObject license = (JSONObject)JSONValue.parseWithException(reader); + tags = (JSONArray)license.get("tags"); + } catch (IOException|ParseException e) { + throw new LicenseGeneratorException(String.format("failure processing FSF metadata for %s", id), e); + } + if (tags == null) { + return null; + } + return tags.contains("libre"); } }