From 0fdeeda070ecaf10bef6dc99d28aa1af370fc620 Mon Sep 17 00:00:00 2001 From: M66B Date: Fri, 1 Dec 2023 11:37:14 +0100 Subject: [PATCH] Added schema.org templates --- ATTRIBUTION.md | 1 + app/build.gradle | 4 + app/src/main/assets/ATTRIBUTION.md | 1 + .../main/assets/schema.org/emailmessage.html | 3 + .../assets/schema.org/flightreservation.html | 20 +++ .../main/java/eu/faircode/email/JsonLd.java | 140 +++++++++++++++--- 6 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 app/src/main/assets/schema.org/emailmessage.html create mode 100644 app/src/main/assets/schema.org/flightreservation.html diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md index ba17bf3859..3f29825bb4 100644 --- a/ATTRIBUTION.md +++ b/ATTRIBUTION.md @@ -49,3 +49,4 @@ FairEmail uses parts or all of: * [DetectHtml](https://github.com/dbennett455/DetectHtml). [The MIT License](https://github.com/dbennett455/DetectHtml/blob/master/LICENSE). * [Liberation Sans Narrow font](https://github.com/liberationfonts/liberation-sans-narrow). Copyright (C) 1989, 1991 Free Software Foundation, Inc. [GNU General Public License version 2 with exceptions](https://fedoraproject.org/wiki/Licensing/LiberationFontLicense). * [Prism](https://github.com/PrismJS/prism). Copyright (c) 2012 Lea Verou. [MIT LICENSE](https://github.com/PrismJS/prism/blob/master/LICENSE). +* [Jayway JsonPath](https://github.com/json-path/JsonPath). Copyright 2011 the original author or authors. [Apache License 2.0](https://github.com/json-path/JsonPath/blob/master/LICENSE). diff --git a/app/build.gradle b/app/build.gradle index c969d439a1..fd1f4d8476 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -516,6 +516,7 @@ dependencies { def billingclient_version = "6.0.1" def javamail_version = "1.6.7" def jsoup_version = "1.16.2" + def jsonpath_version = "2.8.0" def css_version = "0.9.30" def jax_version = "2.3.0-jaxb-1.0.6" def dnsjava_version = "2.1.9" @@ -667,6 +668,9 @@ dependencies { // https://mvnrepository.com/artifact/org.jsoup/jsoup implementation "org.jsoup:jsoup:$jsoup_version" + // https://github.com/json-path/JsonPath + implementation "com.jayway.jsonpath:json-path:$jsonpath_version" + // http://cssparser.sourceforge.net/ // https://mvnrepository.com/artifact/net.sourceforge.cssparser/cssparser implementation "net.sourceforge.cssparser:cssparser:$css_version" diff --git a/app/src/main/assets/ATTRIBUTION.md b/app/src/main/assets/ATTRIBUTION.md index ba17bf3859..3f29825bb4 100644 --- a/app/src/main/assets/ATTRIBUTION.md +++ b/app/src/main/assets/ATTRIBUTION.md @@ -49,3 +49,4 @@ FairEmail uses parts or all of: * [DetectHtml](https://github.com/dbennett455/DetectHtml). [The MIT License](https://github.com/dbennett455/DetectHtml/blob/master/LICENSE). * [Liberation Sans Narrow font](https://github.com/liberationfonts/liberation-sans-narrow). Copyright (C) 1989, 1991 Free Software Foundation, Inc. [GNU General Public License version 2 with exceptions](https://fedoraproject.org/wiki/Licensing/LiberationFontLicense). * [Prism](https://github.com/PrismJS/prism). Copyright (c) 2012 Lea Verou. [MIT LICENSE](https://github.com/PrismJS/prism/blob/master/LICENSE). +* [Jayway JsonPath](https://github.com/json-path/JsonPath). Copyright 2011 the original author or authors. [Apache License 2.0](https://github.com/json-path/JsonPath/blob/master/LICENSE). diff --git a/app/src/main/assets/schema.org/emailmessage.html b/app/src/main/assets/schema.org/emailmessage.html new file mode 100644 index 0000000000..8f2f003a7f --- /dev/null +++ b/app/src/main/assets/schema.org/emailmessage.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/app/src/main/assets/schema.org/flightreservation.html b/app/src/main/assets/schema.org/flightreservation.html new file mode 100644 index 0000000000..9ad464d94c --- /dev/null +++ b/app/src/main/assets/schema.org/flightreservation.html @@ -0,0 +1,20 @@ +
+ Reservation
+ Passenger:
+ Flight:
+ Operated by:
+ Departing: + + () + +
+ Arriving: + + () + +
+ Passenger sequence number:
+ Boarding priority:
+ Boarding policy:
+ Security screening:
+
diff --git a/app/src/main/java/eu/faircode/email/JsonLd.java b/app/src/main/java/eu/faircode/email/JsonLd.java index bcfc559e13..5b9bb3178d 100644 --- a/app/src/main/java/eu/faircode/email/JsonLd.java +++ b/app/src/main/java/eu/faircode/email/JsonLd.java @@ -20,6 +20,13 @@ package eu.faircode.email; */ import android.content.Context; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.PathNotFoundException; import org.json.JSONArray; import org.json.JSONException; @@ -27,7 +34,12 @@ import org.json.JSONObject; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; +import java.util.Locale; // https://json-ld.org/ // https://schema.org/ @@ -38,11 +50,15 @@ public class JsonLd { private Object jroot; private Throwable error = null; - private static final String URI_SCHEMA_ORG = "https://schema.org/"; + private static final String URI_SCHEMA_ORG = "https://schema.org"; + private static final String PLACEHOLDER_START = ""; public JsonLd(String json) { try { - if (json != null && json.trim().startsWith("[")) + if (TextUtils.isEmpty(json)) + jroot = null; + else if (json.trim().startsWith("[")) jroot = new JSONArray(json); else jroot = new JSONObject(json); @@ -52,23 +68,112 @@ public class JsonLd { } } + private String getTemplate(Context context, JSONObject jobject) { + try { + if (!jobject.has("@context") || jobject.isNull("@context")) + return null; + if (!jobject.has("@type") || jobject.isNull("@type")) + return null; + + String jcontext = jobject.getString("@context"); + String jtype = jobject.getString("@type"); + Log.i("JSON-LD template " + jcontext + "=" + jtype); + + if (!URI_SCHEMA_ORG.equals(jcontext) && + !"http://schema.org".equals(jcontext)) { + Log.e("JSON-LD " + jcontext + "?=" + jtype); + return null; + } + + // https://github.com/json-path/JsonPath + + Configuration conf = Configuration.defaultConfiguration(); + Object document = conf.jsonProvider().parse(jobject.toString()); + + String name = "schema.org/" + jtype.toLowerCase(Locale.ROOT) + ".html"; + Log.i("JSON-LD using=" + name); + String template; + try (InputStream is = context.getAssets().open(name)) { + template = Helper.readStream(is); + } catch (FileNotFoundException ex) { + Log.e("JSON-LD " + jcontext + "=" + jtype + "?"); + throw ex; + } + + int start = template.indexOf(PLACEHOLDER_START); + while (start >= 0) { + int end = template.indexOf(PLACEHOLDER_END, start + PLACEHOLDER_START.length()); + if (end < 0) + throw new IllegalArgumentException("Missing placeholder end @" + start); + + String placeholder = template.substring(start + PLACEHOLDER_START.length(), end).trim(); + + String value; + try { + value = JsonPath.read(document, placeholder); + if (value == null) + value = ""; + if (value.startsWith(URI_SCHEMA_ORG + "/")) + value = unCamelCase(value.substring(URI_SCHEMA_ORG.length() + 1)); + } catch (PathNotFoundException ex) { + Log.i(ex); + value = ""; + } + + Log.i("JSON-LD " + placeholder + "=" + value); + template = template.substring(0, start) + value + template.substring(end + PLACEHOLDER_END.length()); + + start = template.indexOf(PLACEHOLDER_START); + } + + return template; + } catch (Throwable ex) { + Log.w("JSON-LD", ex); + return null; + } + } + public String getHtml(Context context) { try { if (error != null) throw error; + if (jroot == null) + throw new IllegalArgumentException("JSON-LD empty"); Document d = Document.createShell(""); d.body().appendElement("hr"); d.body().appendElement("div") - .attr("style", "font-family: monospace;") .appendElement("a") + .attr("style", "font-size: larger !important;") .attr("href", "https://json-ld.org/") .text("Linked data"); d.body().appendElement("br"); + + List jschemas = new ArrayList<>(); + if (jroot instanceof JSONObject) + jschemas.add((JSONObject) jroot); + else if (jroot instanceof JSONArray) { + JSONArray jarray = (JSONArray) jroot; + for (int i = 0; i < jarray.length(); i++) + jschemas.add((JSONObject) jarray.get(i)); + } + + for (JSONObject jschema : jschemas) { + String template = getTemplate(context, jschema); + if (template != null) { + d.body().appendElement("div").append(template); + d.body().append("
"); + } + } + Element holder = d.body().appendElement("div") .attr("style", "font-family: monospace; font-size: smaller !important;"); - getHtml(jroot, 0, holder); + for (int i = 0; i < jschemas.size(); i++) { + if (i > 0) + holder.appendElement("br"); + getHtml(context, jschemas.get(i), 0, holder); + } d.body().appendElement("hr"); return d.body().html(); } catch (Throwable ex) { @@ -79,18 +184,10 @@ public class JsonLd { } } - private void getHtml(Object obj, int indent, Element holder) throws JSONException { + private void getHtml(Context context, Object obj, int indent, Element holder) throws JSONException { if (obj instanceof JSONObject) { JSONObject jobject = (JSONObject) obj; - if (indent == 0 && - jobject.has("@context") && - jobject.has("@type")) { - String context = (jobject.isNull("@context") ? null : jobject.optString("@context")); - String type = (jobject.isNull("@type") ? null : jobject.optString("@type")); - Log.e("JSON-LD " + context + "=" + type); - } - Iterator keys = jobject.keys(); while (keys.hasNext()) { String key = keys.next(); @@ -104,22 +201,19 @@ public class JsonLd { if (v instanceof JSONObject || v instanceof JSONArray) { holder.appendElement("br"); - getHtml(v, indent + 1, holder); + getHtml(context, v, indent + 1, holder); } else { String _v = v.toString(); - if (_v.startsWith(URI_SCHEMA_ORG)) - _v = unCamelCase(_v.substring(URI_SCHEMA_ORG.length())); + if (_v.startsWith(URI_SCHEMA_ORG + "/")) + _v = unCamelCase(_v.substring(URI_SCHEMA_ORG.length() + 1)); holder.appendElement("span").text(_v); holder.appendElement("br"); } } } else if (obj instanceof JSONArray) { JSONArray jarray = (JSONArray) obj; - for (int i = 0; i < jarray.length(); i++) { - if (indent == 0 && i > 0) - holder.appendElement("br"); - getHtml(jarray.get(i), indent, holder); - } + for (int i = 0; i < jarray.length(); i++) + getHtml(context, jarray.get(i), indent, holder); } else { indent(indent, holder); String v = (obj == null ? "" : obj.toString()); @@ -138,7 +232,9 @@ public class JsonLd { sb.append(kar); else { split = true; - sb.append(' ').append(Character.toLowerCase(kar)); + if (i > 0) + kar = Character.toLowerCase(kar); + sb.append(' ').append(kar); } } else { split = false;