From cf4a19f2994fce9560ea56ab14ce10d9c74bc73f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Va=C5=BEan?= Date: Tue, 23 Feb 2021 05:34:37 +0000 Subject: [PATCH 1/3] Expose exceptions in JsonSerializer methods --- .../datamodel/helpers/JsonDeserializer.java | 45 ++++++---- .../datamodel/helpers/JsonSerializer.java | 67 ++++++--------- .../datamodel/helpers/JsonSerializerTest.java | 86 ++++++++++++++++--- .../wdtk/wikibaseapi/StatementUpdateTest.java | 3 +- 4 files changed, 131 insertions(+), 70 deletions(-) diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonDeserializer.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonDeserializer.java index 4cab35b22..f0e3bb880 100644 --- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonDeserializer.java +++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonDeserializer.java @@ -1,5 +1,3 @@ -package org.wikidata.wdtk.datamodel.helpers; - /*- * #%L * Wikidata Toolkit Data Model @@ -20,13 +18,24 @@ * #L% */ -import java.io.IOException; +package org.wikidata.wdtk.datamodel.helpers; -import com.fasterxml.jackson.databind.ObjectReader; -import org.wikidata.wdtk.datamodel.implementation.*; -import org.wikidata.wdtk.datamodel.interfaces.*; +import org.wikidata.wdtk.datamodel.implementation.EntityDocumentImpl; +import org.wikidata.wdtk.datamodel.implementation.EntityRedirectDocumentImpl; +import org.wikidata.wdtk.datamodel.implementation.ItemDocumentImpl; +import org.wikidata.wdtk.datamodel.implementation.LexemeDocumentImpl; +import org.wikidata.wdtk.datamodel.implementation.MediaInfoDocumentImpl; +import org.wikidata.wdtk.datamodel.implementation.PropertyDocumentImpl; +import org.wikidata.wdtk.datamodel.interfaces.EntityDocument; +import org.wikidata.wdtk.datamodel.interfaces.EntityRedirectDocument; +import org.wikidata.wdtk.datamodel.interfaces.ItemDocument; +import org.wikidata.wdtk.datamodel.interfaces.LexemeDocument; +import org.wikidata.wdtk.datamodel.interfaces.MediaInfoDocument; +import org.wikidata.wdtk.datamodel.interfaces.PropertyDocument; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectReader; /** * Helper to deserialize datamodel objects from their @@ -72,55 +81,55 @@ public JsonDeserializer(String siteIri) { /** * Deserializes a JSON string into an {@link ItemDocument}. - * @throws IOException + * @throws JsonProcessingException if the JSON payload is invalid */ - public ItemDocument deserializeItemDocument(String json) throws IOException { + public ItemDocument deserializeItemDocument(String json) throws JsonProcessingException { return itemReader.readValue(json); } /** * Deserializes a JSON string into a {@link PropertyDocument}. - * @throws IOException + * @throws JsonProcessingException if the JSON payload is invalid */ - public PropertyDocument deserializePropertyDocument(String json) throws IOException { + public PropertyDocument deserializePropertyDocument(String json) throws JsonProcessingException { return propertyReader.readValue(json); } /** * Deserializes a JSON string into a {@link LexemeDocument}. - * @throws IOException + * @throws JsonProcessingException if the JSON payload is invalid */ - public LexemeDocument deserializeLexemeDocument(String json) throws IOException { + public LexemeDocument deserializeLexemeDocument(String json) throws JsonProcessingException { return lexemeReader.readValue(json); } /** * Deserializes a JSON string into a {@link MediaInfoDocument}. - * @throws IOException + * @throws JsonProcessingException if the JSON payload is invalid */ - public MediaInfoDocument deserializeMediaInfoDocument(String json) throws IOException { + public MediaInfoDocument deserializeMediaInfoDocument(String json) throws JsonProcessingException { return mediaInfoReader.readValue(json); } /** * Deserializes a JSON string into a {@link EntityDocument}. - * @throws IOException + * @throws JsonProcessingException if the JSON payload is invalid */ - public EntityDocument deserializeEntityDocument(String json) throws IOException { + public EntityDocument deserializeEntityDocument(String json) throws JsonProcessingException { return entityDocumentReader.readValue(json); } /** * Deserializes a JSON string into a {@link EntityRedirectDocument}. - * @throws IOException + * @throws JsonProcessingException if the JSON payload is invalid */ - public EntityRedirectDocument deserializeEntityRedirectDocument(String json) throws IOException { + public EntityRedirectDocument deserializeEntityRedirectDocument(String json) throws JsonProcessingException { return entityRedirectReader.readValue(json); } } diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializer.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializer.java index 369905c2e..977e0e78b 100644 --- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializer.java +++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializer.java @@ -1,5 +1,3 @@ -package org.wikidata.wdtk.datamodel.helpers; - /* * #%L * Wikidata Toolkit Data Model @@ -20,6 +18,8 @@ * #L% */ +package org.wikidata.wdtk.datamodel.helpers; + import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -173,69 +173,54 @@ private void serializeEntityDocument(EntityDocument entityDocument) { } /** - * Serializes the given object in JSON and returns the resulting string. In - * case of errors, null is returned. + * Serializes the given object in JSON and returns the resulting string. + * Throws if the serialization fails. * * @param itemDocument * object to serialize - * @return JSON serialization or null + * @return JSON serialization + * @throws JsonProcessingException if the object cannot be serialized */ - public static String getJsonString(ItemDocument itemDocument) { - return jacksonObjectToString(itemDocument); + public static String getJsonString(ItemDocument itemDocument) throws JsonProcessingException { + return mapper.writeValueAsString(itemDocument); } /** - * Serializes the given object in JSON and returns the resulting string. In - * case of errors, null is returned. + * Serializes the given object in JSON and returns the resulting string. + * Throws if the serialization fails. * * @param propertyDocument * object to serialize - * @return JSON serialization or null + * @return JSON serialization + * @throws JsonProcessingException if the object cannot be serialized */ - public static String getJsonString(PropertyDocument propertyDocument) { - return jacksonObjectToString(propertyDocument); + public static String getJsonString(PropertyDocument propertyDocument) throws JsonProcessingException { + return mapper.writeValueAsString(propertyDocument); } /** - * Serializes the given object in JSON and returns the resulting string. In - * case of errors, null is returned. + * Serializes the given object in JSON and returns the resulting string. + * Throws if the serialization fails. * * @param mediaInfoDocument * object to serialize - * @return JSON serialization or null + * @return JSON serialization + * @throws JsonProcessingException if the object cannot be serialized */ - public static String getJsonString(MediaInfoDocument mediaInfoDocument) { - return jacksonObjectToString(mediaInfoDocument); + public static String getJsonString(MediaInfoDocument mediaInfoDocument) throws JsonProcessingException { + return mapper.writeValueAsString(mediaInfoDocument); } /** - * Serializes the given object in JSON and returns the resulting string. In - * case of errors, null is returned. + * Serializes the given object in JSON and returns the resulting string. + * Throws if the serialization fails. * * @param statement * object to serialize - * @return JSON serialization or null - */ - public static String getJsonString(Statement statement) { - return jacksonObjectToString(statement); - } - - /** - * Serializes the given object in JSON and returns the resulting string. In - * case of errors, null is returned. In particular, this happens if the - * object is not based on a Jackson-annotated class. An error is logged in - * this case. - * - * @param object - * object to serialize - * @return JSON serialization or null + * @return JSON serialization + * @throws JsonProcessingException if the object cannot be serialized */ - protected static String jacksonObjectToString(Object object) { - try { - return mapper.writeValueAsString(object); - } catch (JsonProcessingException e) { - logger.error("Failed to serialize JSON data: " + e.toString()); - return null; - } + public static String getJsonString(Statement statement) throws JsonProcessingException { + return mapper.writeValueAsString(statement); } } diff --git a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializerTest.java b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializerTest.java index 3e64c35c8..76a725ffa 100644 --- a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializerTest.java +++ b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/JsonSerializerTest.java @@ -21,7 +21,7 @@ package org.wikidata.wdtk.datamodel.helpers; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -29,6 +29,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; import org.wikidata.wdtk.datamodel.implementation.EntityDocumentImpl; @@ -37,10 +39,14 @@ import org.wikidata.wdtk.datamodel.interfaces.EntityDocument; import org.wikidata.wdtk.datamodel.interfaces.ItemDocument; import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue; +import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue; import org.wikidata.wdtk.datamodel.interfaces.PropertyDocument; +import org.wikidata.wdtk.datamodel.interfaces.SiteLink; import org.wikidata.wdtk.datamodel.interfaces.Statement; +import org.wikidata.wdtk.datamodel.interfaces.StatementGroup; import org.wikidata.wdtk.datamodel.interfaces.StatementRank; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; @@ -97,7 +103,7 @@ public void testSerializer() throws IOException { } @Test - public void testItemDocumentToJson() { + public void testItemDocumentToJson() throws JsonProcessingException { ItemDocument id = Datamodel.makeItemDocument( Datamodel.makeWikidataItemIdValue("Q42"), Collections.emptyList(), @@ -110,7 +116,7 @@ public void testItemDocumentToJson() { } @Test - public void testPropertyDocumentToJson() { + public void testPropertyDocumentToJson() throws JsonProcessingException { PropertyDocument pd = Datamodel.makePropertyDocument( Datamodel.makeWikidataPropertyIdValue("P1"), Collections. emptyList(), @@ -123,7 +129,7 @@ Collections. emptyList(), } @Test - public void testStatementToJson() { + public void testStatementToJson() throws JsonProcessingException { Statement s = Datamodel.makeStatement(ItemIdValue.NULL, Datamodel.makeNoValueSnak(Datamodel.makeWikidataPropertyIdValue("P1")), Collections.emptyList(), Collections.emptyList(), @@ -134,14 +140,74 @@ public void testStatementToJson() { @Test public void testJacksonObjectToJsonError() { - Object obj = new Object() { - @SuppressWarnings("unused") - public String getData() { - throw new RuntimeException("Test exception"); + ItemDocument obj = new ItemDocument() { + + @Override + public List getStatementGroups() { + return null; + } + + @Override + public long getRevisionId() { + return 0; + } + + @Override + public Map getLabels() { + return null; + } + + @Override + public Map getDescriptions() { + return null; + } + + @Override + public Map> getAliases() { + return null; + } + + @Override + public ItemDocument withoutStatementIds(Set statementIds) { + return null; + } + + @Override + public ItemDocument withStatement(Statement statement) { + return null; + } + + @Override + public ItemDocument withRevisionId(long newRevisionId) { + return null; + } + + @Override + public ItemDocument withLabel(MonolingualTextValue newLabel) { + return null; + } + + @Override + public ItemDocument withDescription(MonolingualTextValue newDescription) { + return null; + } + + @Override + public ItemDocument withAliases(String language, List aliases) { + return null; + } + + @Override + public Map getSiteLinks() { + return null; + } + + @Override + public ItemIdValue getEntityId() { + return null; } }; - String result = JsonSerializer.jacksonObjectToString(obj); - assertNull(result); + assertThrows(JsonProcessingException.class, () -> JsonSerializer.getJsonString(obj)); } } diff --git a/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/StatementUpdateTest.java b/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/StatementUpdateTest.java index 3f9a56a54..8eb0d70bd 100644 --- a/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/StatementUpdateTest.java +++ b/wdtk-wikibaseapi/src/test/java/org/wikidata/wdtk/wikibaseapi/StatementUpdateTest.java @@ -42,6 +42,7 @@ import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.StatementRank; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -215,7 +216,7 @@ public void testUpdateStatement() { } @Test - public void testAddStatements() { + public void testAddStatements() throws JsonProcessingException { // Inserting new P2 statements won't touch existing P1 statement Statement s1 = StatementBuilder.forSubjectAndProperty(Q1, P1) .withValue(Q1).withId("ID-s1").build(); From f5d4d990f0670775a64316fbb712744c50862de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Va=C5=BEan?= Date: Tue, 23 Feb 2021 05:56:39 +0000 Subject: [PATCH 2/3] Expose exceptions in WbGetEntitiesAction --- .../wdtk/wikibaseapi/WbGetEntitiesAction.java | 116 +++++++++--------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbGetEntitiesAction.java b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbGetEntitiesAction.java index 30e080294..4378f4169 100644 --- a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbGetEntitiesAction.java +++ b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbGetEntitiesAction.java @@ -1,5 +1,3 @@ -package org.wikidata.wdtk.wikibaseapi; - /* * #%L * Wikidata Toolkit Wikibase API @@ -20,21 +18,31 @@ * #L% */ +package org.wikidata.wdtk.wikibaseapi; + import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import com.fasterxml.jackson.databind.DeserializationFeature; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.wikidata.wdtk.datamodel.helpers.Datamodel; import org.wikidata.wdtk.datamodel.helpers.DatamodelMapper; import org.wikidata.wdtk.datamodel.implementation.EntityDocumentImpl; import org.wikidata.wdtk.datamodel.implementation.EntityIdValueImpl; -import org.wikidata.wdtk.datamodel.interfaces.*; +import org.wikidata.wdtk.datamodel.interfaces.EntityDocument; +import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; +import org.wikidata.wdtk.datamodel.interfaces.ItemDocument; +import org.wikidata.wdtk.datamodel.interfaces.MediaInfoDocument; +import org.wikidata.wdtk.datamodel.interfaces.MediaInfoIdValue; +import org.wikidata.wdtk.datamodel.interfaces.SiteLink; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -46,9 +54,6 @@ */ public class WbGetEntitiesAction { - static final Logger logger = LoggerFactory - .getLogger(WbGetEntitiesAction.class); - /** * Connection to an Wikibase API. */ @@ -94,6 +99,8 @@ public WbGetEntitiesAction(ApiConnection connection, String siteIri) { * if the API returns an error * @throws IOException * if we encounter network issues or HTTP 500 errors from Wikibase + * @throws MalformedResponseException + * if one of the returned entities cannot be parsed */ public Map wbGetEntities( WbGetEntitiesActionData properties) @@ -112,8 +119,8 @@ public Map wbGetEntities( * 500 for bots. This limit may also apply to the number of language codes * and sites used for filtering. *

- * If errors occur (e.g., exceptions trying to access the Web API), then the - * errors will be logged and an empty collection will be returned. + * If an error occurs (e.g., exceptions trying to access the Web API), + * the exception will be propagated to the caller. * * @param ids * list of ids of entities for which data should be retrieved @@ -150,6 +157,8 @@ public Map wbGetEntities( * if we encounter network errors, or HTTP 500 errors on Wikibase's side * @throws IllegalArgumentException * if the given combination of parameters does not make sense + * @throws MalformedResponseException + * if one of the returned entities cannot be parsed */ public Map wbGetEntities(String ids, String sites, String titles, String props, String languages, String sitefilter) @@ -190,56 +199,51 @@ public Map wbGetEntities(String ids, String sites, Map result = new HashMap<>(); - try { - JsonNode root = this.connection.sendJsonRequest("POST", parameters); - - JsonNode entities = root.path("entities"); - Iterator> entitiesIterator = entities.fields(); - int i = 0; - while(entitiesIterator.hasNext()) { - Entry entry = entitiesIterator.next(); - JsonNode entityNode = entry.getValue(); - if(!entityNode.has("missing")) { - try { - EntityDocument ed = mapper.reader() - .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) - .treeToValue(entityNode, EntityDocumentImpl.class); - - if (titles == null) { - // We use the JSON key rather than the id of the value - // so that retrieving redirected entities works. - result.put(entry.getKey(), ed); - } else { - if (ed instanceof ItemDocument) { - SiteLink siteLink = ((ItemDocument) ed).getSiteLinks().get(sites); - if(siteLink != null) { - result.put(siteLink.getPageTitle(), ed); - } - } else if(ed instanceof MediaInfoDocument) { - result.put(entityNode.get("title").textValue(), ed); + JsonNode root = this.connection.sendJsonRequest("POST", parameters); + + JsonNode entities = root.path("entities"); + Iterator> entitiesIterator = entities.fields(); + int i = 0; + while(entitiesIterator.hasNext()) { + Entry entry = entitiesIterator.next(); + JsonNode entityNode = entry.getValue(); + if(!entityNode.has("missing")) { + try { + EntityDocument ed = mapper.reader() + .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) + .treeToValue(entityNode, EntityDocumentImpl.class); + + if (titles == null) { + // We use the JSON key rather than the id of the value + // so that retrieving redirected entities works. + result.put(entry.getKey(), ed); + } else { + if (ed instanceof ItemDocument) { + SiteLink siteLink = ((ItemDocument) ed).getSiteLinks().get(sites); + if(siteLink != null) { + result.put(siteLink.getPageTitle(), ed); } + } else if(ed instanceof MediaInfoDocument) { + result.put(entityNode.get("title").textValue(), ed); } - } catch (JsonProcessingException e) { - logger.error("Error when reading JSON for entity " - + entityNode.path("id").asText("UNKNOWN") - + ": " + e.toString()); } - } else if(entityNode.has("id")) { - try { - EntityIdValue entityIdValue = EntityIdValueImpl.fromId(entityNode.get("id").asText(), siteIri); - if(entityIdValue instanceof MediaInfoIdValue) { - //TODO: bad hack, it would be much nicer if the API would return the page title - result.put(titlesList.get(i), Datamodel.makeMediaInfoDocument((MediaInfoIdValue) entityIdValue)); - } - } catch (IllegalArgumentException e) { - logger.warn("Invalid entity id returned: " + entityNode.get("id").asText()); + } catch (JsonProcessingException e) { + throw new MalformedResponseException( + "Error when reading JSON for entity " + entityNode.path("id").asText("UNKNOWN"), e); + } + } else if(entityNode.has("id")) { + try { + EntityIdValue entityIdValue = EntityIdValueImpl.fromId(entityNode.get("id").asText(), siteIri); + if(entityIdValue instanceof MediaInfoIdValue) { + //TODO: bad hack, it would be much nicer if the API would return the page title + result.put(titlesList.get(i), Datamodel.makeMediaInfoDocument((MediaInfoIdValue) entityIdValue)); } + } catch (IllegalArgumentException e) { + throw new MalformedResponseException( + "Invalid entity id returned: " + entityNode.get("id").asText(), e); } - i++; } - } catch (IOException e) { - logger.error("Could not retrive data: " + e.toString()); - throw e; + i++; } return result; From dc0e35a8268257debf9fbe5a3e22abe2a5362af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Va=C5=BEan?= Date: Tue, 23 Feb 2021 06:06:10 +0000 Subject: [PATCH 3/3] Expose exceptions in WbSearchEntitiesAction --- .../wikibaseapi/WbSearchEntitiesAction.java | 258 +++++++++--------- 1 file changed, 127 insertions(+), 131 deletions(-) diff --git a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbSearchEntitiesAction.java b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbSearchEntitiesAction.java index 04c0dbf81..bab63c08d 100644 --- a/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbSearchEntitiesAction.java +++ b/wdtk-wikibaseapi/src/main/java/org/wikidata/wdtk/wikibaseapi/WbSearchEntitiesAction.java @@ -1,5 +1,3 @@ -package org.wikidata.wdtk.wikibaseapi; - /* * #%L * Wikidata Toolkit Wikibase API @@ -20,14 +18,14 @@ * #L% */ +package org.wikidata.wdtk.wikibaseapi; + import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException; import com.fasterxml.jackson.core.JsonProcessingException; @@ -42,132 +40,130 @@ */ public class WbSearchEntitiesAction { - private static final Logger LOGGER = LoggerFactory - .getLogger(WbSearchEntitiesAction.class); - - /** - * Connection to a Wikibase API. - */ - private final ApiConnection connection; - - /** - * Mapper object used for deserializing JSON data. - */ - private final ObjectMapper mapper = new ObjectMapper(); - - /** - * Creates an object to fetch data from the given ApiConnection. The site - * URI is necessary since it is not contained in the data retrieved from the - * API. - * - * @param connection - * {@link ApiConnection} Object to send the requests - * @param siteUri - * the URI identifying the site that is accessed (usually the - * prefix of entity URIs), e.g., - * "http://www.wikidata.org/entity/" - */ - public WbSearchEntitiesAction(ApiConnection connection, String siteUri) { - this.connection = connection; - } - - public List wbSearchEntities(WbGetEntitiesSearchData properties) - throws MediaWikiApiErrorException, IOException { - return wbSearchEntities(properties.search, properties.language, - properties.strictlanguage, properties.type, properties.limit, properties.offset); - } - - /** - * Executes the API action "wbsearchentity" for the given parameters. - * Searches for entities using labels and aliases. Returns a label and - * description for the entity in the user language if possible. Returns - * details of the matched term. The matched term text is also present in the - * aliases key if different from the display label. - * - *

- * See the online API documentation for further information. - *

- * - * @param search - * (required) search for this text - * @param language - * (required) search in this language - * @param strictLanguage - * (optional) whether to disable language fallback - * @param type - * (optional) search for this type of entity - * One of the following values: item, property - * Default: item - * @param limit - * (optional) maximal number of results - * no more than 50 (500 for bots) allowed - * Default: 7 - * @param offset - * (optional) offset where to continue a search - * Default: 0 - * this parameter is called "continue" in the API (which is a Java keyword) - * - * @return list of matching entities retrieved via the API URL - * @throws MediaWikiApiErrorException - * if the API returns an error - * @throws IllegalArgumentException - * if the given combination of parameters does not make sense - */ - public List wbSearchEntities(String search, String language, - Boolean strictLanguage, String type, Long limit, Long offset) - throws MediaWikiApiErrorException, IOException { - - Map parameters = new HashMap<>(); - parameters.put(ApiConnection.PARAM_ACTION, "wbsearchentities"); - - if (search != null) { - parameters.put("search", search); - } else { - throw new IllegalArgumentException( - "Search parameter must be specified for this action."); - } - - if (language != null) { - parameters.put("language", language); - } else { - throw new IllegalArgumentException( - "Language parameter must be specified for this action."); - } - if (strictLanguage != null) { - parameters.put("strictlanguage", Boolean.toString(strictLanguage)); - } - - if (type != null) { - parameters.put("type", type); - } - - if (limit != null) { - parameters.put("limit", Long.toString(limit)); - } - - if (offset != null) { - parameters.put("continue", Long.toString(offset)); - } - - List results = new ArrayList<>(); - - JsonNode root = this.connection.sendJsonRequest("POST", parameters); - JsonNode entities = root.path("search"); - for (JsonNode entityNode : entities) { - try { - JacksonWbSearchEntitiesResult ed = mapper.treeToValue(entityNode, - JacksonWbSearchEntitiesResult.class); - results.add(ed); - } catch (JsonProcessingException e) { - LOGGER.error("Error when reading JSON for entity " - + entityNode.path("id").asText("UNKNOWN") + ": " - + e.toString()); - } - } - - return results; - } + /** + * Connection to a Wikibase API. + */ + private final ApiConnection connection; + + /** + * Mapper object used for deserializing JSON data. + */ + private final ObjectMapper mapper = new ObjectMapper(); + + /** + * Creates an object to fetch data from the given ApiConnection. The site + * URI is necessary since it is not contained in the data retrieved from the + * API. + * + * @param connection + * {@link ApiConnection} Object to send the requests + * @param siteUri + * the URI identifying the site that is accessed (usually the + * prefix of entity URIs), e.g., + * "http://www.wikidata.org/entity/" + */ + public WbSearchEntitiesAction(ApiConnection connection, String siteUri) { + this.connection = connection; + } + + public List wbSearchEntities(WbGetEntitiesSearchData properties) + throws MediaWikiApiErrorException, IOException { + return wbSearchEntities(properties.search, properties.language, + properties.strictlanguage, properties.type, properties.limit, properties.offset); + } + + /** + * Executes the API action "wbsearchentity" for the given parameters. + * Searches for entities using labels and aliases. Returns a label and + * description for the entity in the user language if possible. Returns + * details of the matched term. The matched term text is also present in the + * aliases key if different from the display label. + * + *

+ * See the online API documentation for further information. + *

+ * + * @param search + * (required) search for this text + * @param language + * (required) search in this language + * @param strictLanguage + * (optional) whether to disable language fallback + * @param type + * (optional) search for this type of entity + * One of the following values: item, property + * Default: item + * @param limit + * (optional) maximal number of results + * no more than 50 (500 for bots) allowed + * Default: 7 + * @param offset + * (optional) offset where to continue a search + * Default: 0 + * this parameter is called "continue" in the API (which is a Java keyword) + * + * @return list of matching entities retrieved via the API URL + * @throws MediaWikiApiErrorException + * if the API returns an error + * @throws IllegalArgumentException + * if the given combination of parameters does not make sense + * @throws MalformedResponseException + * if response JSON cannot be parsed + */ + public List wbSearchEntities(String search, String language, + Boolean strictLanguage, String type, Long limit, Long offset) + throws MediaWikiApiErrorException, IOException { + + Map parameters = new HashMap<>(); + parameters.put(ApiConnection.PARAM_ACTION, "wbsearchentities"); + + if (search != null) { + parameters.put("search", search); + } else { + throw new IllegalArgumentException( + "Search parameter must be specified for this action."); + } + + if (language != null) { + parameters.put("language", language); + } else { + throw new IllegalArgumentException( + "Language parameter must be specified for this action."); + } + if (strictLanguage != null) { + parameters.put("strictlanguage", Boolean.toString(strictLanguage)); + } + + if (type != null) { + parameters.put("type", type); + } + + if (limit != null) { + parameters.put("limit", Long.toString(limit)); + } + + if (offset != null) { + parameters.put("continue", Long.toString(offset)); + } + + List results = new ArrayList<>(); + + JsonNode root = this.connection.sendJsonRequest("POST", parameters); + JsonNode entities = root.path("search"); + for (JsonNode entityNode : entities) { + try { + JacksonWbSearchEntitiesResult ed = mapper.treeToValue(entityNode, + JacksonWbSearchEntitiesResult.class); + results.add(ed); + } catch (JsonProcessingException e) { + throw new MalformedResponseException( + "Error when reading JSON for entity " + entityNode.path("id").asText("UNKNOWN"), e); + } + } + + return results; + } }