This fixes #124.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
-public class Core implements IdentityListener, UpdateListener {
+public class Core implements IdentityListener, UpdateListener, SoneProvider, PostProvider {
/**
* Enumeration for the possible states of a {@link Sone}.
* @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);
* 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);
/**
* {@inheritDoc}
*/
+ @Override
public boolean validate(T value) {
return (validator == null) || (value == null) || validator.validate(value);
}
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import net.pterodactylus.sone.data.Post;
+
+/**
+ * Interface for objects that can provide {@link Post}s by their ID.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+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);
+
+}
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.core;
+
+import net.pterodactylus.sone.data.Sone;
+
+/**
+ * Interface for objects that can provide {@link Sone}s by their ID.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+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);
+
+}
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;
}
- /** 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;
}
//
}
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 {
}
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)) + "…";
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;
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”
*/
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.
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());
}
}
+ /**
+ * 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<Sone> getMentionedSones(String text) {
/* we need no context to find mentioned Sones. */
Set<Sone> mentionedSones = new HashSet<Sone>();
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.text;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit test case for {@link SoneTextParser}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+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<Part> 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<Part> 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<Part> 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();
+ }
+
+}