Also, extract parsers for the different identity formats of WOT.
--- /dev/null
+package net.pterodactylus.fcp.plugin;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Generates an {@link Identity}.
+ *
+ * @param <I> The type of identity to generate
+ */
+@FunctionalInterface
+interface IdentityGenerator<I extends Identity> {
+
+ /**
+ * Generates an identity from the given parameters
+ *
+ * @param identity The ID of the identity
+ * @param nickname The nickname of the identity
+ * @param requestUri The request URI of the identity
+ * @param insertUri The insert URI of the identity
+ * @param contexts The contexts of the identity
+ * @param properties The properties of the identity
+ * @return The generated identity
+ */
+ I createIdentity(String identity, String nickname, String requestUri, String insertUri, Set<String> contexts, Map<String, String> properties);
+
+}
--- /dev/null
+package net.pterodactylus.fcp.plugin;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface for parsers for WebOfTrust’s on-wire identity formats.
+ */
+interface IdentityParser {
+
+ /**
+ * Whether this parser can parse an identity from the given fields.
+ *
+ * @param fields The fields to parse the identity from
+ * @param prefix A prefix used for all accessed fields, may be an empty String
+ * @return {@code true} if this parse can parse identities from the given
+ * fields, {@code false} otherwise
+ */
+ boolean canParse(Map<String, String> fields, String prefix);
+
+ /**
+ * Parses an identity from the given fields.
+ *
+ * @param fields The fields to parse the identity from
+ * @param prefix A prefix used for all access fields, may be an empty String
+ * @param identityGenerator An identity generator
+ * @param <I> The type of the identity to parse
+ * @return The generated identity
+ */
+ <I extends Identity> I parseSingleIdentity(Map<String, String> fields, String prefix, IdentityGenerator<I> identityGenerator);
+
+ /**
+ * Parses multiple identities from the given fields.
+ *
+ * @param fields The fields to parse the identities from
+ * @param prefix A prefix used for all identities, may be an empty String
+ * @param identityGenerator An identity generator
+ * @param <I> The type of the identity to parse
+ * @return The generated identities
+ */
+ <I extends Identity> Set<I> parseMultipleIdentities(Map<String, String> fields, String prefix, IdentityGenerator<I> identityGenerator);
+
+}
--- /dev/null
+package net.pterodactylus.fcp.plugin;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * {@link IdentityParser} for the first WebOfTrust identity format. This
+ * format is now deprecated for most of the transferred identities, but not
+ * all of them; e.g. {@code GetOwnIdentities} still uses this format.
+ */
+class IdentityParserV1 implements IdentityParser {
+
+ @Override
+ public boolean canParse(Map<String, String> fields, String prefix) {
+ return fields.containsKey(prefix + "Identity0");
+ }
+
+ @Override
+ public <I extends Identity> I parseSingleIdentity(Map<String, String> fields, String prefix, IdentityGenerator<I> identityGenerator) {
+ return parseIdentity(fields, field -> prefix + field, identityGenerator);
+ }
+
+ @Override
+ public <I extends Identity> Set<I> parseMultipleIdentities(Map<String, String> fields, String prefix, IdentityGenerator<I> identityGenerator) {
+ Set<I> identities = new HashSet<>();
+ for (int identityIndex = 0; fields.containsKey(prefix + "Identity" + identityIndex); identityIndex++) {
+ int index = identityIndex;
+ identities.add(parseIdentity(fields, field -> prefix + field + index, identityGenerator));
+ }
+ return identities;
+ }
+
+ private static <I extends Identity> I parseIdentity(Map<String, String> fields, Function<String, String> fieldPackager, IdentityGenerator<I> identityGenerator) {
+ String id = fields.get(fieldPackager.apply("Identity"));
+ String name = fields.get(fieldPackager.apply("Nickname"));
+ String requestUri = fields.get(fieldPackager.apply("RequestURI"));
+ String insertUri = fields.get(fieldPackager.apply("InsertURI"));
+ Set<String> contexts = new HashSet<>();
+ for (int contextIndex = 0; fields.containsKey(fieldPackager.apply("Contexts") + ".Context" + contextIndex); contextIndex++) {
+ contexts.add(fields.get(fieldPackager.apply("Contexts") + ".Context" + contextIndex));
+ }
+ Map<String, String> properties = new HashMap<>();
+ for (int propertyIndex = 0; fields.containsKey(fieldPackager.apply("Properties") + ".Property" + propertyIndex + ".Name"); propertyIndex++) {
+ String key = fields.get(fieldPackager.apply("Properties") + ".Property" + propertyIndex + ".Name");
+ String value = fields.get(fieldPackager.apply("Properties") + ".Property" + propertyIndex + ".Value");
+ properties.put(key, value);
+ }
+ return identityGenerator.createIdentity(id, name, requestUri, insertUri, contexts, properties);
+ }
+
+}
--- /dev/null
+package net.pterodactylus.fcp.plugin;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * {@link IdentityParser} for the second (and current) WebOfTrust identity
+ * format. This format is more consequent in how the data is structured, but
+ * it is not yet used for all WebOfTrust messages.
+ */
+class IdentityParserV2 implements IdentityParser {
+
+ @Override
+ public boolean canParse(Map<String, String> fields, String prefix) {
+ return fields.containsKey(prefix + "Identities.0.ID");
+ }
+
+ @Override
+ public <I extends Identity> I parseSingleIdentity(Map<String, String> fields, String prefix, IdentityGenerator<I> identityGenerator) {
+ return parseIdentity(fields, field -> prefix + field, identityGenerator);
+ }
+
+ @Override
+ public <I extends Identity> Set<I> parseMultipleIdentities(Map<String, String> fields, String prefix, IdentityGenerator<I> identityGenerator) {
+ Set<I> identities = new HashSet<>();
+ for (int identityIndex = 0; fields.containsKey(prefix + "Identities." + identityIndex + ".ID"); identityIndex++) {
+ int index = identityIndex;
+ identities.add(parseIdentity(fields, field -> prefix + "Identities." + index + "." + field, identityGenerator));
+ }
+ return identities;
+ }
+
+ private <I extends Identity> I parseIdentity(Map<String, String> fields, Function<String, String> fieldPackager, IdentityGenerator<I> identityGenerator) {
+ String id = fields.get(fieldPackager.apply("ID"));
+ String name = fields.get(fieldPackager.apply("Nickname"));
+ String requestUri = fields.get(fieldPackager.apply("RequestURI"));
+ String insertUri = fields.get(fieldPackager.apply("InsertURI"));
+ Set<String> contexts = new HashSet<>();
+ for (int contextIndex = 0; fields.containsKey(fieldPackager.apply("Contexts.") + contextIndex + ".Name"); contextIndex++) {
+ contexts.add(fields.get(fieldPackager.apply("Contexts.") + contextIndex + ".Name"));
+ }
+ Map<String, String> properties = new HashMap<>();
+ for (int propertyIndex = 0; fields.containsKey(fieldPackager.apply("Properties.") + propertyIndex + ".Name"); propertyIndex++) {
+ String key = fields.get(fieldPackager.apply("Properties.") + propertyIndex + ".Name");
+ String value = fields.get(fieldPackager.apply("Properties.") + propertyIndex + ".Value");
+ properties.put(key, value);
+ }
+ return identityGenerator.createIdentity(id, name, requestUri, insertUri, contexts, properties);
+ }
+
+}
package net.pterodactylus.fcp.plugin;
+import net.pterodactylus.fcp.highlevel.FcpClient;
+import net.pterodactylus.fcp.highlevel.FcpException;
+
import java.io.IOException;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import net.pterodactylus.fcp.highlevel.FcpClient;
-import net.pterodactylus.fcp.highlevel.FcpException;
-
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
* if an FCP error occurs
*/
public Set<Identity> getIdentitesByScore(OwnIdentity ownIdentity, String context, Boolean positive) throws IOException, FcpException {
- Map<String, String> replies = fcpClient.sendPluginMessage(webOfTrustPluginName, createParameters("Message", "GetIdentitiesByScore", "TreeOwner", ownIdentity.getIdentifier(), "Context", context, "Selection", ((positive == null) ? "0" : (positive ? "+" : "-"))));
+ Map<String, String> replies = fcpClient.sendPluginMessage(webOfTrustPluginName, createParameters("Message", "GetIdentitiesByScore", "Truster", ownIdentity.getIdentifier(), "Context", context, "Selection", ((positive == null) ? "0" : (positive ? "+" : "-"))));
if (!replies.get("Message").equals("Identities")) {
throw new FcpException("WebOfTrust Plugin did not reply with “Identities” message!");
}
- Set<Identity> identities = new HashSet<Identity>();
- for (int identityIndex = 1; replies.containsKey("Identity" + identityIndex); identityIndex++) {
- String identifier = replies.get("Identity" + identityIndex);
- String nickname = replies.get("Nickname" + identityIndex);
- String requestUri = replies.get("RequestURI" + identityIndex);
- identities.add(new Identity(identifier, nickname, requestUri, emptySet(), emptyMap()));
- }
- return identities;
+ return parseIdentities(replies, WebOfTrustPlugin::createIdentity);
}
/**
return parameterMap;
}
+ private static <I extends Identity> Set<I> parseIdentities(Map<String, String> replies, IdentityGenerator<I> identityGenerator) {
+ IdentityParser parser = v2IdentityParser.canParse(replies, "") ? v2IdentityParser : v1IdentityParser;
+ return parser.parseMultipleIdentities(replies, "", identityGenerator);
+ }
+
+ private static final IdentityParser v1IdentityParser = new IdentityParserV1();
+ private static final IdentityParser v2IdentityParser = new IdentityParserV2();
+
+ private static Identity createIdentity(String identity, String nickname, String requestUri, @SuppressWarnings("unused") String insertUri, Set<String> contexts, Map<String, String> properties) {
+ return new Identity(identity, nickname, requestUri, contexts, properties);
+ }
+
+ private static OwnIdentity createOwnIdentity(String identity, String nickname, String requestUri, String insertUri, Set<String> contexts, Map<String, String> properties) {
+ return new OwnIdentity(identity, nickname, requestUri, insertUri, contexts, properties);
+ }
+
private static final String webOfTrustPluginName = "plugins.WebOfTrust.WebOfTrust";
}
import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
}
+ public static class GetIdentitiesByScoreTests extends Common {
+
+ @Test
+ public void getIdentitiesByScoreSendsCorrectMessageWhenPositiveIsNull() throws Exception {
+ verifySentMessageHasCorrectSelectionValueForPositiveValue(null, "0");
+ }
+
+ @Test
+ public void getIdentitiesByScoreSendsCorrectMessageWhenPositiveIsTrue() throws Exception {
+ verifySentMessageHasCorrectSelectionValueForPositiveValue(true, "+");
+ }
+
+ @Test
+ public void getIdentitiesByScoreSendsCorrectMessageWhenPositiveIsFalse() throws Exception {
+ verifySentMessageHasCorrectSelectionValueForPositiveValue(false, "-");
+ }
+
+ private void verifySentMessageHasCorrectSelectionValueForPositiveValue(Boolean positive, String selectionValue) throws Exception {
+ TestFcpConnection fcpConnection = createConnectionThatSendsIdentitiesByScore();
+ WebOfTrustPlugin webOfTrustPlugin = createWebOfTrustPlugin(fcpConnection);
+ OwnIdentity ownIdentity = new OwnIdentity("test-owner", "Test Owner", "request-uri", "insert-uri", emptySet(), emptyMap());
+ webOfTrustPlugin.getIdentitesByScore(ownIdentity, "test", positive);
+ assertThat(fcpConnection.sentMessages.get(0), allOf(
+ isNamed(equalTo("FCPPluginMessage")),
+ hasField("Identifier", notNullValue()),
+ hasField("PluginName", equalTo("plugins.WebOfTrust.WebOfTrust")),
+ hasField("Param.Message", equalTo("GetIdentitiesByScore")),
+ hasField("Param.Truster", equalTo("test-owner")),
+ hasField("Param.Context", equalTo("test")),
+ hasField("Param.Selection", equalTo(selectionValue))
+ ));
+ }
+
+ @Test
+ public void getIdentitiesByScoreThrowsExceptionWhenDifferentReplyIsSentByPlugin() {
+ FcpConnection fcpConnection = createConnectionThatSendsOtherMessage();
+ WebOfTrustPlugin webOfTrustPlugin = createWebOfTrustPlugin(fcpConnection);
+ OwnIdentity ownIdentity = new OwnIdentity("test-owner", "Test Owner", "request-uri", "insert-uri", emptySet(), emptyMap());
+ assertThrows(FcpException.class, () -> webOfTrustPlugin.getIdentitesByScore(ownIdentity, "test", true));
+ }
+
+ @Test
+ public void getIdentitiesByScoreParsesReturnedIdentitiesCorrectly() throws Exception {
+ TestFcpConnection fcpConnection = createConnectionThatSendsIdentitiesByScore();
+ WebOfTrustPlugin webOfTrustPlugin = createWebOfTrustPlugin(fcpConnection);
+ OwnIdentity ownIdentity = new OwnIdentity("test-owner", "Test Owner", "request-uri", "insert-uri", emptySet(), emptyMap());
+ Collection<Identity> identities = webOfTrustPlugin.getIdentitesByScore(ownIdentity, "test", true);
+ assertThat(identities, containsInAnyOrder(
+ allOf(
+ isIdentity(equalTo("id1"), equalTo("ID 1"), equalTo("request-1")),
+ hasContexts(containsInAnyOrder("test1", "test2")),
+ hasProperties(allOf(
+ aMapWithSize(2),
+ hasEntry("prop1", "value1"),
+ hasEntry("prop2", "value2")
+ ))
+ ),
+ allOf(
+ isIdentity(equalTo("id2"), equalTo("ID 2"), equalTo("request-2")),
+ hasContexts(containsInAnyOrder("test2", "test3")),
+ hasProperties(allOf(
+ aMapWithSize(2),
+ hasEntry("prop3", "value3"),
+ hasEntry("prop4", "value4")
+ ))
+ )
+ ));
+ }
+
+ private TestFcpConnection createConnectionThatSendsIdentitiesByScore() {
+ return createConnection("Identities",
+ prefixed("Identities.0.", "Type", "Identity", "Nickname", "ID 1", "RequestURI", "request-1", "ID", "id1", "VersionID", "4-4", "PublishesTrustList", "true"),
+ prefixed("Identities.0.Contexts.", "0.Name", "test1", "1.Name", "test2", "Amount", "2"),
+ prefixed("Identities.0.Properties.", "0.Name", "prop1", "0.Value", "value1", "1.Name", "prop2", "1.Value", "value2", "Amounts", "2"),
+ prefixed("Identities.1.", "Type", "Identity", "Nickname", "ID 2", "RequestURI", "request-2", "ID", "id2", "VersionID", "5-5", "PublishesTrustList", "false"),
+ prefixed("Identities.1.Contexts.", "0.Name", "test2", "1.Name", "test3", "Amount", "2"),
+ prefixed("Identities.1.Properties.", "0.Name", "prop3", "0.Value", "value3", "1.Name", "prop4", "1.Value", "value4", "Amounts", "2"),
+ prefixed("Scores.0.", "Truster", "test-owner", "Trustee", "id1", "Capacity", "10", "Rank", "2", "Value", "25", "VersionID", "3-3"),
+ prefixed("Scores.1.", "Truster", "test-owner", "Trustee", "id2", "Capacity", "15", "Rank", "1", "Value", "50", "VersionID", "6-6"),
+ entries("Scores.Amount", "1", "Identities.Amount", "2")
+ );
+ }
+
+ }
+
private static class Common {
@SafeVarargs
return new TestFcpConnection(messageConsumer);
}
- protected List<Entry<String, String>> entries(String... replies) {
+ protected List<Entry<String, String>> prefixed(String prefix, String... replies) {
List<Entry<String, String>> entries = new ArrayList<>();
for (int replyIndex = 0; replyIndex < replies.length - 1; replyIndex += 2) {
- entries.add(new SimpleEntry<>(replies[replyIndex], replies[replyIndex + 1]));
+ entries.add(new SimpleEntry<>(prefix + replies[replyIndex], replies[replyIndex + 1]));
}
return entries;
}
+ protected List<Entry<String, String>> entries(String... replies) {
+ return prefixed("", replies);
+ }
+
@Rule
public final Timeout timeout = Timeout.seconds(5);