From: David ‘Bombe’ Roden Date: Thu, 9 Jun 2011 10:33:49 +0000 (+0200) Subject: Use provider interfaces for the parser; enhance test case. X-Git-Tag: 0.6.5^2~8 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=282916447c6f35a5c461c58011c3fa7d1343cecc;hp=9d50dcb65cec8d10306fbdb0d73826f33af2afb9 Use provider interfaces for the parser; enhance test case. This fixes #124. --- diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 1e68c63..1c67231 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -65,7 +65,7 @@ import freenet.keys.FreenetURI; * * @author David ‘Bombe’ Roden */ -public class Core implements IdentityListener, UpdateListener { +public class Core implements IdentityListener, UpdateListener, SoneProvider, PostProvider { /** * Enumeration for the possible states of a {@link Sone}. @@ -352,6 +352,7 @@ public class Core implements IdentityListener, UpdateListener { * @return The Sone with the given ID, or {@code null} if there is no such * Sone */ + @Override public Sone getSone(String id, boolean create) { if (isLocalSone(id)) { return getLocalSone(id); @@ -574,6 +575,7 @@ public class Core implements IdentityListener, UpdateListener { * exists, {@code false} to return {@code null} * @return The post, or {@code null} if there is no such post */ + @Override public Post getPost(String postId, boolean create) { synchronized (posts) { Post post = posts.get(postId); diff --git a/src/main/java/net/pterodactylus/sone/core/Options.java b/src/main/java/net/pterodactylus/sone/core/Options.java index 7392da2..1bdf21f 100644 --- a/src/main/java/net/pterodactylus/sone/core/Options.java +++ b/src/main/java/net/pterodactylus/sone/core/Options.java @@ -193,6 +193,7 @@ public class Options { /** * {@inheritDoc} */ + @Override public boolean validate(T value) { return (validator == null) || (value == null) || validator.validate(value); } diff --git a/src/main/java/net/pterodactylus/sone/core/PostProvider.java b/src/main/java/net/pterodactylus/sone/core/PostProvider.java new file mode 100644 index 0000000..edede44 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/PostProvider.java @@ -0,0 +1,43 @@ +/* + * Sone - PostProvider.java - Copyright © 2011 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core; + +import net.pterodactylus.sone.data.Post; + +/** + * Interface for objects that can provide {@link Post}s by their ID. + * + * @author David ‘Bombe’ Roden + */ +public interface PostProvider { + + /** + * Returns the post with the given ID, if it exists. If it does not exist + * and {@code create} is {@code false}, {@code null} is returned; otherwise, + * a new post with the given ID is created and returned. + * + * @param postId + * The ID of the post to return + * @param create + * {@code true} to create a new post if no post with the given ID + * exists, {@code false} to return {@code null} instead + * @return The post with the given ID, or {@code null} + */ + public Post getPost(String postId, boolean create); + +} diff --git a/src/main/java/net/pterodactylus/sone/core/SoneProvider.java b/src/main/java/net/pterodactylus/sone/core/SoneProvider.java new file mode 100644 index 0000000..dcf0f61 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/core/SoneProvider.java @@ -0,0 +1,43 @@ +/* + * Sone - SoneProvider.java - Copyright © 2011 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core; + +import net.pterodactylus.sone.data.Sone; + +/** + * Interface for objects that can provide {@link Sone}s by their ID. + * + * @author David ‘Bombe’ Roden + */ +public interface SoneProvider { + + /** + * Returns the Sone with the given ID, if it exists. If it does not exist + * and {@code create} is {@code false}, {@code null} is returned; otherwise, + * a new Sone with the given ID is created and returned. + * + * @param soneId + * The ID of the Sone to return + * @param create + * {@code true} to create a new Sone if no Sone with the given ID + * exists, {@code false} to return {@code null} instead + * @return The Sone with the given ID, or {@code null} + */ + public Sone getSone(String soneId, boolean create); + +} diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index 98bb800..94c3db4 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -26,7 +26,8 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.pterodactylus.sone.core.Core; +import net.pterodactylus.sone.core.PostProvider; +import net.pterodactylus.sone.core.SoneProvider; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.util.logging.Logging; @@ -78,17 +79,23 @@ public class SoneTextParser implements Parser { } - /** The core. */ - private final Core core; + /** The Sone provider. */ + private final SoneProvider soneProvider; + + /** The post provider. */ + private final PostProvider postProvider; /** * Creates a new freenet link parser. * - * @param core - * The core + * @param soneProvider + * The Sone provider + * @param postProvider + * The post provider */ - public SoneTextParser(Core core) { - this.core = core; + public SoneTextParser(SoneProvider soneProvider, PostProvider postProvider) { + this.soneProvider = soneProvider; + this.postProvider = postProvider; } // @@ -174,7 +181,7 @@ public class SoneTextParser implements Parser { } if (line.length() >= (next + 7 + 43)) { String soneId = line.substring(next + 7, next + 50); - Sone sone = core.getSone(soneId, false); + Sone sone = soneProvider.getSone(soneId, false); if (sone != null) { parts.add(new SonePart(sone)); } else { @@ -193,7 +200,7 @@ public class SoneTextParser implements Parser { } if (line.length() >= (next + 7 + 36)) { String postId = line.substring(next + 7, next + 43); - Post post = core.getPost(postId, false); + Post post = postProvider.getPost(postId, false); if ((post != null) && (post.getSone() != null)) { String postText = post.getText(); postText = postText.substring(0, Math.min(postText.length(), 20)) + "…"; @@ -218,7 +225,9 @@ public class SoneTextParser implements Parser { if (!lastLineEmpty && lineComplete) { parts.add(new PlainTextPart("\n" + line.substring(0, next))); } else { - parts.add(new PlainTextPart(line.substring(0, next))); + if (next > 0) { + parts.add(new PlainTextPart(line.substring(0, next))); + } } String link = line.substring(next, nextSpace); String name = link; diff --git a/src/main/java/net/pterodactylus/sone/text/TextFilter.java b/src/main/java/net/pterodactylus/sone/text/TextFilter.java index 5744368..9e3587c 100644 --- a/src/main/java/net/pterodactylus/sone/text/TextFilter.java +++ b/src/main/java/net/pterodactylus/sone/text/TextFilter.java @@ -17,10 +17,6 @@ package net.pterodactylus.sone.text; -import java.util.logging.Logger; - -import net.pterodactylus.util.logging.Logging; - /** * Filter for newly inserted text. This filter strips HTTP links to the local * node of identifying marks, e.g. a link to “http://localhost:8888/KSK@gpl.txt” @@ -32,9 +28,6 @@ import net.pterodactylus.util.logging.Logging; */ public class TextFilter { - /** The logger. */ - private static final Logger logger = Logging.getLogger(TextFilter.class); - /** * Filters the given text, stripping the host header part for links to the * local node. diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 8eb0122..c9547fb 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -196,7 +196,7 @@ public class WebInterface implements CoreListener { public WebInterface(SonePlugin sonePlugin) { this.sonePlugin = sonePlugin; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); - soneTextParser = new SoneTextParser(getCore()); + soneTextParser = new SoneTextParser(getCore(), getCore()); templateContextFactory = new TemplateContextFactory(); templateContextFactory.addAccessor(Object.class, new ReflectionAccessor()); @@ -668,6 +668,15 @@ public class WebInterface implements CoreListener { } } + /** + * Returns all {@link Core#isLocalSone(Sone) local Sone}s that are + * referenced by {@link SonePart}s in the given text (after parsing it using + * {@link SoneTextParser}). + * + * @param text + * The text to parse + * @return All mentioned local Sones + */ private Set getMentionedSones(String text) { /* we need no context to find mentioned Sones. */ Set mentionedSones = new HashSet(); diff --git a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java new file mode 100644 index 0000000..0343f05 --- /dev/null +++ b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java @@ -0,0 +1,130 @@ +/* + * Sone - SoneTextParserTest.java - Copyright © 2011 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.text; + +import java.io.IOException; +import java.io.StringReader; + +import junit.framework.TestCase; + +/** + * JUnit test case for {@link SoneTextParser}. + * + * @author David ‘Bombe’ Roden + */ +public class SoneTextParserTest extends TestCase { + + // + // ACTIONS + // + + /** + * Tests basic plain-text operation of the parser. + * + * @throws IOException + * if an I/O error occurs + */ + public void testPlainText() throws IOException { + SoneTextParser soneTextParser = new SoneTextParser(null, null); + Iterable parts; + + /* check basic operation. */ + parts = soneTextParser.parse(null, new StringReader("Test.")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "Test.", convertText(parts, PlainTextPart.class)); + + /* check empty lines at start and end. */ + parts = soneTextParser.parse(null, new StringReader("\nTest.\n\n")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "Test.", convertText(parts, PlainTextPart.class)); + + /* check duplicate empty lines in the text. */ + parts = soneTextParser.parse(null, new StringReader("\nTest.\n\n\nTest.")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "Test.\n\nTest.", convertText(parts, PlainTextPart.class)); + } + + /** + * Tests parsing of KSK links. + * + * @throws IOException + * if an I/O error occurs + */ + public void testKSKLinks() throws IOException { + SoneTextParser soneTextParser = new SoneTextParser(null, null); + Iterable parts; + + /* check basic links. */ + parts = soneTextParser.parse(null, new StringReader("KSK@gpl.txt")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "[KSK@gpl.txt|gpl.txt|gpl.txt]", convertText(parts, FreenetLinkPart.class)); + + /* check embedded links. */ + parts = soneTextParser.parse(null, new StringReader("Link is KSK@gpl.txt\u200b.")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "Link is [KSK@gpl.txt|gpl.txt|gpl.txt]\u200b.", convertText(parts, PlainTextPart.class, FreenetLinkPart.class)); + + /* check embedded links and line breaks. */ + parts = soneTextParser.parse(null, new StringReader("Link is KSK@gpl.txt\nKSK@test.dat\n")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "Link is [KSK@gpl.txt|gpl.txt|gpl.txt]\n[KSK@test.dat|test.dat|test.dat]", convertText(parts, PlainTextPart.class, FreenetLinkPart.class)); + } + + // + // PRIVATE METHODS + // + + /** + * Converts all given {@link Part}s into a string, validating that the + * part’s classes match only the expected classes. + * + * @param parts + * The parts to convert to text + * @param validClasses + * The valid classes; if no classes are given, all classes are + * valid + * @return The converted text + */ + private String convertText(Iterable parts, Class... validClasses) { + StringBuilder text = new StringBuilder(); + for (Part part : parts) { + assertNotNull("Part", part); + boolean classValid = validClasses.length == 0; + for (Class validClass : validClasses) { + if (validClass.isAssignableFrom(part.getClass())) { + classValid = true; + break; + } + } + if (!classValid) { + assertEquals("Part’s Class", null, part.getClass()); + } + if (part instanceof PlainTextPart) { + text.append(((PlainTextPart) part).getText()); + } else if (part instanceof FreenetLinkPart) { + FreenetLinkPart freenetLinkPart = (FreenetLinkPart) part; + text.append('[').append(freenetLinkPart.getLink()).append('|').append(freenetLinkPart.isTrusted() ? "trusted|" : "").append(freenetLinkPart.getTitle()).append('|').append(freenetLinkPart.getText()).append(']'); + } else if (part instanceof LinkPart) { + LinkPart linkPart = (LinkPart) part; + text.append('[').append(linkPart.getLink()).append('|').append(linkPart.getTitle()).append('|').append(linkPart.getText()).append(']'); + } + } + return text.toString(); + } + +}