• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.pump.provider;
18 
19 import android.net.Uri;
20 import android.text.TextUtils;
21 
22 import androidx.annotation.NonNull;
23 import androidx.annotation.WorkerThread;
24 
25 import com.android.pump.util.Clog;
26 import com.android.pump.util.Http;
27 
28 import org.json.JSONArray;
29 import org.json.JSONException;
30 import org.json.JSONObject;
31 import org.json.JSONTokener;
32 
33 import java.io.IOException;
34 import java.nio.charset.StandardCharsets;
35 import java.util.ArrayList;
36 import java.util.Iterator;
37 import java.util.List;
38 
39 @WorkerThread
40 public final class Wikidata {
41     private static final String TAG = Clog.tag(Wikidata.class);
42 
Wikidata()43     private Wikidata() { }
44 
search(@onNull Query query)45     public static void search(@NonNull Query query) throws IOException {
46         search(query, 1);
47     }
48 
search(@onNull Query query, int maxResults)49     public static void search(@NonNull Query query, int maxResults) throws IOException {
50         Clog.i(TAG, "search(" + query + ", " + maxResults + ")");
51         getContentForResults(getSearchResults(getQueryString(query), maxResults));
52     }
53 
getQueryString(Query query)54     private static String getQueryString(Query query) {
55         StringBuilder builder = new StringBuilder();
56         builder.append(query.getName());
57         if (query.hasYear()) {
58             builder.append(' ');
59             builder.append(query.getYear());
60         }
61         if (query.isEpisode()) {
62             builder.append(' ');
63             builder.append('s');
64             builder.append(query.getSeason());
65 
66             builder.append(' ');
67             builder.append('e');
68             builder.append(query.getEpisode());
69         }
70         return  builder.toString();
71     }
72 
getSearchResults(String search, int maxResults)73     private static List<String> getSearchResults(String search, int maxResults) throws IOException {
74         String uri = getSearchUri(search, maxResults);
75         Clog.i(TAG, uri);
76         String result = new String(Http.get(uri), StandardCharsets.UTF_8);
77         Clog.i(TAG, result);
78         try {
79             Object root = new JSONTokener(result).nextValue();
80             dumpJson(root);
81             JSONObject resultRoot = (JSONObject) root;
82             JSONArray resultArray = resultRoot.getJSONObject("query").getJSONArray("search");
83             List<String> ids = new ArrayList<>(resultArray.length());
84             for (int i = 0; i < resultArray.length(); ++i) {
85                 ids.add(resultArray.getJSONObject(i).getString("title"));
86             }
87             return ids;
88         } catch (JSONException e) {
89             Clog.w(TAG, "Failed to parse search result", e);
90             throw new IOException(e);
91         }
92     }
93 
getContentForResults(List<String> ids)94     private static void getContentForResults(List<String> ids) throws IOException {
95         /*
96         String uri = getContentUri(ids);
97         Clog.i(TAG, uri);
98         String result = new String(Http.get(uri), StandardCharsets.UTF_8);
99         //Clog.i(TAG, result);
100         try {
101             Object root = new JSONTokener(result).nextValue();
102             //dumpJson(root);
103             JSONObject resultRoot = (JSONObject) root;
104         } catch (JSONException e) {
105             Clog.w(TAG, "Failed to parse data", e);
106             throw new IOException(e);
107         }
108         */
109         getSparqlForResults(ids);
110     }
111 
getSparqlForResults(List<String> ids)112     private static void getSparqlForResults(List<String> ids) throws IOException {
113         String uri = getSparqlUri(ids);
114         Clog.i(TAG, uri);
115         String result = new String(Http.get(uri), StandardCharsets.UTF_8);
116         Clog.i(TAG, result);
117         try {
118             Object root = new JSONTokener(result).nextValue();
119             dumpJson(root);
120             JSONObject resultRoot = (JSONObject) root;
121         } catch (JSONException e) {
122             Clog.w(TAG, "Failed to parse sparql", e);
123             throw new IOException(e);
124         }
125     }
126 
getSearchUri(String search, int maxResults)127     private static String getSearchUri(String search, int maxResults) {
128         Uri.Builder ub = new Uri.Builder();
129         ub.scheme("https");
130         ub.authority("www.wikidata.org");
131         ub.appendPath("w");
132         ub.appendPath("api.php");
133         ub.appendQueryParameter("action", "query");
134         ub.appendQueryParameter("list", "search");
135         ub.appendQueryParameter("format", "json");
136         ub.appendQueryParameter("formatversion", "2");
137         ub.appendQueryParameter("srsearch", search);
138         ub.appendQueryParameter("srlimit", Integer.toString(maxResults));
139         ub.appendQueryParameter("srprop", "");
140         ub.appendQueryParameter("srinfo", "");
141         return ub.build().toString();
142     }
143 
144     /*
145      */
146 
147     // https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service
148 
149     /*
150 const endpointUrl = 'https://query.wikidata.org/sparql',
151       sparqlQuery = `SELECT DISTINCT ?item ?itemLabel ?itemDescription
152 WHERE
153 {
154   ?item wdt:P31/wdt:P279* wd:Q11424.
155   SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en" }
156   ?item wdt:P3383 ?itemPoster.
157 }`,
158       fullUrl = endpointUrl + '?query=' + encodeURIComponent( sparqlQuery ),
159       headers = { 'Accept': 'application/sparql-results+json' };
160 
161 fetch( fullUrl, { headers } ).then( body => body.json() ).then( json => {
162     const { head: { vars }, results } = json;
163     for ( const result of results.bindings ) {
164         for ( const variable of vars ) {
165             console.log( '%s: %o', variable, result[variable] );
166         }
167         console.log( '---' );
168     }
169 } );
170      */
171 
172     // ?action=wbgetentities&ids=Q775450|Q3041294|Q646968|Q434841|Q11920&format=jsonfm&props=labels&languages=en|de|fr
getContentUri(List<String> ids)173     private static String getContentUri(List<String> ids) {
174         Uri.Builder ub = new Uri.Builder();
175         ub.scheme("https");
176         ub.authority("www.wikidata.org");
177         ub.appendPath("w");
178         ub.appendPath("api.php");
179         ub.appendQueryParameter("action", "wbgetentities");
180         ub.appendQueryParameter("props", "labels|descriptions");
181         ub.appendQueryParameter("format", "json");
182         ub.appendQueryParameter("formatversion", "2");
183         ub.appendQueryParameter("languages", "en");
184         ub.appendQueryParameter("languagefallback", "");
185         ub.appendQueryParameter("ids", TextUtils.join("|", ids));
186         return ub.build().toString();
187     }
188 
getSparqlUri(List<String> ids)189     private static String getSparqlUri(List<String> ids) {
190         List<String> dbIds = new ArrayList<>(ids.size());
191         for (String id : ids) {
192             dbIds.add("wd:" + id);
193         }
194         String sparqlQuery = ""
195                 + "SELECT * WHERE {"
196                 +   "VALUES ?item {"
197                 +     TextUtils.join(" ", dbIds)
198                 +   "}"
199                 +   "FILTER EXISTS {?item wdt:P31/wdt:P279* wd:Q11424.}"
200                 +   "OPTIONAL {?item wdt:P3383 ?poster.}"
201                 +   "?item rdfs:label ?title FILTER (lang(?title) = 'en')."
202                 + "}";
203         Uri.Builder ub = new Uri.Builder();
204         ub.scheme("https");
205         ub.authority("query.wikidata.org");
206         ub.appendPath("sparql");
207         ub.appendQueryParameter("format", "json");
208         ub.appendQueryParameter("query", sparqlQuery);
209         return ub.build().toString();
210     }
211 
dumpJson(Object root)212     private static void dumpJson(Object root) throws JSONException {
213         dumpJson(null, "", root);
214     }
215 
dumpJson(String name, String indent, Object object)216     private static void dumpJson(String name, String indent, Object object) throws JSONException {
217         name = name != null ? name + ": " : "";
218         if (object == JSONObject.NULL) {
219             Clog.d(TAG, indent + name + "null");
220         } else if (object instanceof JSONObject) {
221             Clog.d(TAG, indent + name + "{");
222             JSONObject jsonObject = (JSONObject) object;
223             Iterator<String> keys = jsonObject.keys();
224             while (keys.hasNext()) {
225                 String key = keys.next();
226                 dumpJson(key, indent + "  ", jsonObject.get(key));
227             }
228             Clog.d(TAG, indent + "}");
229         } else if (object instanceof JSONArray) {
230             Clog.d(TAG, indent + name + "[");
231             JSONArray jsonArray = (JSONArray) object;
232             for (int i = 0; i < jsonArray.length(); ++i) {
233                 dumpJson(null, indent + "  ", jsonArray.get(i));
234             }
235             Clog.d(TAG, indent + "]");
236         } else if (object instanceof String) {
237             String jsonString = (String) object;
238             Clog.d(TAG, indent + name + jsonString + " (string)");
239         } else if (object instanceof Boolean) {
240             Boolean jsonBoolean = (Boolean) object;
241             Clog.d(TAG, indent + name + jsonBoolean + " (boolean)");
242         } else if (object instanceof Integer) {
243             Integer jsonInteger = (Integer) object;
244             Clog.d(TAG, indent + name + jsonInteger + " (int)");
245         } else if (object instanceof Long) {
246             Long jsonLong = (Long) object;
247             Clog.d(TAG, indent + name + jsonLong + " (long)");
248         } else if (object instanceof Double) {
249             Double jsonDouble = (Double) object;
250             Clog.d(TAG, indent + name + jsonDouble + " (double)");
251         }
252     }
253 }
254