package jalview.analytics;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.UUID;
-public class GoogleAnalytics4 {
- private static final String JALVIEW_ID = "Jalview Desktop";
- private static final String SESSION_ID = new Random().toString();
- private static final String MEASUREMENT_ID = "G-6TMPHMXEQ0";
- private static final String BASE_URL = "https://www.google-analytics.com/mp/collect";
-
- private List<Map.Entry<String, String>> queryStringValues;
- private List<Map.Entry<String, Object>> jsonValues;
- private List<Map.Entry<String, String>> cookies;
-
- private void addToJson(String key, List<String> values) {
- }
-
- private void addToJson(String key, String value) {
- }
-
- private void addToJson(String key, int value) {
- }
-
- private void addToJson(String key, boolean value) {
- }
-
- private void addToJson(String key, Object[] array) {
- }
-
- private void addQueryStringValue(String key, String value) {
- }
-
- private void reset() {
- jsonValues = new ArrayList<>();
- queryStringValues = new ArrayList<>();
- cookies = new ArrayList<>();
- }
-
- private String buildJson() {
- StringBuilder sb = new StringBuilder();
- int indent = 0;
- sb.append("{");
- indent++;
-
- sb.append("}");
- return sb.toString();
- }
-
- private void addJsonObject(StringBuilder sb, int indent, String key, Object value) {
- indent(sb, indent);
- sb.append('"').append(key).append('"');
- sb.append(": ");
- if (List.class.equals(value.getClass())) {
- sb.append('\n');
- }
- addJsonValue(sb, indent, value);
- sb.append(",\n");
- }
-
- private void addJsonValue(StringBuilder sb, int indent, Object value) {
- if (value == null) {
- return;
- }
- Class c = value.getClass();
- if (String.class.equals(c)) {
- } else if (Integer.class.equals(c)) {
- } else if (Boolean.class.equals(c)) {
- }
- sb.append(value);
- }
-
- private void indent(StringBuilder sb, int indent) {
- sb.append(" ".repeat(indent));
- }
+import jalview.bin.Console;
+
+public class GoogleAnalytics4
+{
+ private static final String JALVIEW_ID = "Jalview Desktop";
+
+ private static final String SESSION_ID = new Random().toString();
+
+ private static final String MEASUREMENT_ID = "G-6TMPHMXEQ0";
+
+ private static final String API_SECRET = "Qb9NSbqkRDqizG6j2BBJ2g";
+
+ // This will generate a different CLIENT_ID each time the application is
+ // launched. Do we want to store it in .jalview_properties?
+ private static final String CLIENT_ID = UUID.randomUUID().toString();
+
+ private static final String BASE_URL = "https://www.google-analytics.com/mp/collect";
+
+ private List<Map.Entry<String, String>> queryStringValues;
+
+ private List<Map.Entry<String, Object>> jsonValues;
+
+ private List<Map.Entry<String, String>> cookieValues;
+
+ public GoogleAnalytics4()
+ {
+ this.reset();
+ }
+
+ public void sendAnalytics()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(BASE_URL);
+ sb.append('?');
+ sb.append(buildQueryString());
+ try
+ {
+ URL url = new URL(sb.toString());
+ URLConnection urlConnection = url.openConnection();
+ HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
+ httpURLConnection.setRequestMethod("POST");
+ httpURLConnection.setDoOutput(true);
+
+ byte[] jsonBytes = buildJson().getBytes(StandardCharsets.UTF_8);
+ int jsonLength = jsonBytes.length;
+
+ httpURLConnection.setFixedLengthStreamingMode(jsonLength);
+ httpURLConnection.setRequestProperty("Content-Type",
+ "application/json; charset=UTF-8");
+ httpURLConnection.connect();
+ try (OutputStream os = httpURLConnection.getOutputStream())
+ {
+ os.write(jsonBytes);
+ }
+
+ } catch (MalformedURLException e)
+ {
+ Console.debug(
+ "Somehow the GoogleAnalytics4 BASE_URL and queryString is malformed.",
+ e);
+ return;
+ } catch (IOException e)
+ {
+ Console.debug("Connection to GoogleAnalytics4 BASE_URL '" + BASE_URL
+ + "' failed.", e);
+ } catch (ClassCastException e)
+ {
+ Console.debug(
+ "Couldn't cast URLConnection to HttpURLConnection in GoogleAnalytics4.",
+ e);
+ }
+
+ }
+
+ private void addToJson(String key, List<Object> values)
+ {
+ jsonValues.add(jsonEntry(key, values));
+ }
+
+ private void addToJson(String key, String value)
+ {
+ jsonValues.add(jsonEntry(key, value));
+ }
+
+ private void addToJson(String key, int value)
+ {
+ jsonValues.add(jsonEntry(key, Integer.valueOf(value)));
+ }
+
+ private void addToJson(String key, boolean value)
+ {
+ jsonValues.add(jsonEntry(key, Boolean.valueOf(value)));
+ }
+
+ private void addQueryStringValue(String key, String value)
+ {
+ queryStringValues.add(qsEntry(key, value));
+ }
+
+ private void addCookieValue(String key, String value)
+ {
+ cookieValues.add(qsEntry(key, value));
+ }
+
+ public void reset()
+ {
+ jsonValues = new ArrayList<>();
+ queryStringValues = new ArrayList<>();
+ cookieValues = new ArrayList<>();
+ }
+
+ private String buildQueryString()
+ {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, String> entry : queryStringValues)
+ {
+ if (sb.length() > 0)
+ {
+ sb.append('&');
+ }
+ sb.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
+ sb.append('=');
+ sb.append(
+ URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
+ }
+ return sb.toString();
+ }
+
+ private void buildCookieHeaders()
+ {
+ // TODO not needed yet
+ }
+
+ private String buildJson()
+ {
+ StringBuilder sb = new StringBuilder();
+ int indent = 0;
+ sb.append("{");
+ for (Map.Entry<String, Object> entry : jsonValues)
+ {
+
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private void addJsonObject(StringBuilder sb, int indent,
+ Map.Entry<String, Object> entry)
+ {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ indent(sb, indent);
+ sb.append('"').append(key).append('"');
+ sb.append(": ");
+ if (List.class.equals(value.getClass()))
+ {
+ sb.append('\n');
+ }
+ addJsonValue(sb, indent, value);
+ sb.append(",\n");
+ }
+
+ private void addJsonValue(StringBuilder sb, int indent, Object value)
+ {
+ if (value == null)
+ {
+ return;
+ }
+ try
+ {
+ Class<? extends Object> c = value.getClass();
+ if (Map.Entry.class.equals(c))
+ {
+ Map.Entry<String, Object> object = (Map.Entry<String, Object>) value;
+ addJsonObject(sb, indent + 1, object);
+ }
+ else if (List.class.equals(c))
+ {
+ indent(sb, indent);
+ sb.append("[\n");
+ for (Object v : (List<Object>) value)
+ {
+ indent(sb, indent + 1);
+ addJsonValue(sb, indent, v);
+ sb.append(",\n");
+ }
+ indent(sb, indent);
+ sb.append("]");
+ }
+ else if (String.class.equals(c))
+ {
+ sb.append('"');
+ sb.append((String) value);
+ sb.append('"');
+ }
+ else if (Integer.class.equals(c))
+ {
+ sb.append(((Integer) value).toString());
+ }
+ else if (Boolean.class.equals(c))
+ {
+ sb.append(((Boolean) value).toString());
+ }
+ } catch (ClassCastException e)
+ {
+ Console.debug(
+ "Could not deal with type of jsonObject " + value.toString(),
+ e);
+ }
+ }
+
+ private void indent(StringBuilder sb, int indent)
+ {
+ sb.append(" ".repeat(indent));
+ }
+
+ private static Map.Entry<String, Object> jsonEntry(String s, Object o)
+ {
+ return new AbstractMap.SimpleEntry<String, Object>(s, o);
+ }
+
+ private static Map.Entry<String, String> qsEntry(String s, String v)
+ {
+ return new AbstractMap.SimpleEntry<String, String>(s, v);
+ }
}