From ffb2ea1773cf7e3d1b7fc41ab0e9c3c1eed514e0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 9 Sep 2014 19:09:33 +0200 Subject: [PATCH] Move album parsing to new configuration parser. --- .../sone/core/ConfigurationSoneParser.java | 65 +++++++ .../java/net/pterodactylus/sone/core/Core.java | 51 ++---- src/test/java/net/pterodactylus/sone/Matchers.java | 81 +++++++++ .../sone/core/ConfigurationSoneParserTest.java | 200 +++++++++++++++++++++ 4 files changed, 360 insertions(+), 37 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java b/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java index 39dcd12..7b7be9f 100644 --- a/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java +++ b/src/main/java/net/pterodactylus/sone/core/ConfigurationSoneParser.java @@ -1,14 +1,20 @@ package net.pterodactylus.sone.core; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.Nullable; +import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.AlbumBuilderFactory; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostBuilderFactory; import net.pterodactylus.sone.database.PostReplyBuilder; @@ -175,8 +181,67 @@ public class ConfigurationSoneParser { return friends; } + public List parseTopLevelAlbums( + AlbumBuilderFactory albumBuilderFactory) { + Map albums = new HashMap(); + List topLevelAlbums = new ArrayList(); + int albumCounter = 0; + while (true) { + String albumPrefix = "/Albums/" + albumCounter++; + String albumId = getString(albumPrefix + "/ID", null); + if (albumId == null) { + break; + } + String albumTitle = getString(albumPrefix + "/Title", null); + String albumDescription = + getString(albumPrefix + "/Description", null); + String albumParentId = getString(albumPrefix + "/Parent", null); + String albumImageId = + getString(albumPrefix + "/AlbumImage", null); + if ((albumTitle == null) || (albumDescription == null)) { + throw new InvalidAlbumFound(); + } + Album album = albumBuilderFactory.newAlbumBuilder() + .withId(albumId) + .by(sone) + .build() + .modify() + .setTitle(albumTitle) + .setDescription(albumDescription) + .setAlbumImage(albumImageId) + .update(); + if (albumParentId != null) { + Album parentAlbum = albums.get(albumParentId); + if (parentAlbum == null) { + throw new InvalidParentAlbumFound(albumParentId); + } + parentAlbum.addAlbum(album); + } else { + topLevelAlbums.add(album); + } + albums.put(albumId, album); + } + return topLevelAlbums; + } + public static class InvalidPostFound extends RuntimeException { } public static class InvalidPostReplyFound extends RuntimeException { } + public static class InvalidAlbumFound extends RuntimeException { } + + public static class InvalidParentAlbumFound extends RuntimeException { + + private final String albumParentId; + + public InvalidParentAlbumFound(String albumParentId) { + this.albumParentId = albumParentId; + } + + public String getAlbumParentId() { + return albumParentId; + } + + } + } diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index a6b4c96..e08acd1 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -20,6 +20,7 @@ package net.pterodactylus.sone.core; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.not; +import static java.lang.String.format; import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER; import java.net.MalformedURLException; @@ -38,6 +39,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidAlbumFound; +import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound; import net.pterodactylus.sone.core.Options.DefaultOption; @@ -1124,43 +1127,17 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, Set friends = configurationSoneParser.parseFriends(); /* load albums. */ - List topLevelAlbums = new ArrayList(); - int albumCounter = 0; - while (true) { - String albumPrefix = sonePrefix + "/Albums/" + albumCounter++; - String albumId = configuration.getStringValue(albumPrefix + "/ID").getValue(null); - if (albumId == null) { - break; - } - String albumTitle = configuration.getStringValue(albumPrefix + "/Title").getValue(null); - String albumDescription = configuration.getStringValue(albumPrefix + "/Description").getValue(null); - String albumParentId = configuration.getStringValue(albumPrefix + "/Parent").getValue(null); - String albumImageId = configuration.getStringValue(albumPrefix + "/AlbumImage").getValue(null); - if ((albumTitle == null) || (albumDescription == null)) { - logger.log(Level.WARNING, "Invalid album found, aborting load!"); - return; - } - Album album = database.newAlbumBuilder() - .withId(albumId) - .by(sone) - .build() - .modify() - .setTitle(albumTitle) - .setDescription(albumDescription) - .setAlbumImage(albumImageId) - .update(); - if (albumParentId != null) { - Album parentAlbum = getAlbum(albumParentId); - if (parentAlbum == null) { - logger.log(Level.WARNING, String.format("Invalid parent album ID: %s", albumParentId)); - return; - } - parentAlbum.addAlbum(album); - } else { - if (!topLevelAlbums.contains(album)) { - topLevelAlbums.add(album); - } - } + List topLevelAlbums; + try { + topLevelAlbums = + configurationSoneParser.parseTopLevelAlbums(database); + } catch (InvalidAlbumFound iaf) { + logger.log(Level.WARNING, "Invalid album found, aborting load!"); + return; + } catch (InvalidParentAlbumFound ipaf) { + logger.log(Level.WARNING, format("Invalid parent album ID: %s", + ipaf.getAlbumParentId())); + return; } /* load images. */ diff --git a/src/test/java/net/pterodactylus/sone/Matchers.java b/src/test/java/net/pterodactylus/sone/Matchers.java index 41eb0dd..c73866e 100644 --- a/src/test/java/net/pterodactylus/sone/Matchers.java +++ b/src/test/java/net/pterodactylus/sone/Matchers.java @@ -22,6 +22,7 @@ import static java.util.regex.Pattern.compile; import java.io.IOException; import java.io.InputStream; +import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; @@ -101,6 +102,86 @@ public class Matchers { return new PostReplyMatcher(postReplyId, postId, time, text); } + public static Matcher isAlbum(final String albumId, + final String parentAlbumId, + final String title, final String albumDescription, + final String imageId) { + return new TypeSafeDiagnosingMatcher() { + @Override + protected boolean matchesSafely(Album album, + Description mismatchDescription) { + if (!album.getId().equals(albumId)) { + mismatchDescription.appendText("ID is ") + .appendValue(album.getId()); + return false; + } + if (parentAlbumId == null) { + if (album.getParent() != null) { + mismatchDescription.appendText("has parent album"); + return false; + } + } else { + if (album.getParent() == null) { + mismatchDescription.appendText("has no parent album"); + return false; + } + if (!album.getParent().getId().equals(parentAlbumId)) { + mismatchDescription.appendText("parent album is ") + .appendValue(album.getParent().getId()); + return false; + } + } + if (!title.equals(album.getTitle())) { + mismatchDescription.appendText("has title ") + .appendValue(album.getTitle()); + return false; + } + if (!albumDescription.equals(album.getDescription())) { + mismatchDescription.appendText("has description ") + .appendValue(album.getDescription()); + return false; + } + if (imageId == null) { + if (album.getAlbumImage() != null) { + mismatchDescription.appendText("has album image"); + return false; + } + } else { + if (album.getAlbumImage() == null) { + mismatchDescription.appendText("has no album image"); + return false; + } + if (!album.getAlbumImage().getId().equals(imageId)) { + mismatchDescription.appendText("has album image ") + .appendValue(album.getAlbumImage().getId()); + return false; + } + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("is album ").appendValue(albumId); + if (parentAlbumId == null) { + description.appendText(", has no parent"); + } else { + description.appendText(", has parent ") + .appendValue(parentAlbumId); + } + description.appendText(", has title ").appendValue(title); + description.appendText(", has description ") + .appendValue(albumDescription); + if (imageId == null) { + description.appendText(", has no album image"); + } else { + description.appendText(", has album image ") + .appendValue(imageId); + } + } + }; + } + private static class PostMatcher extends TypeSafeDiagnosingMatcher { private final String postId; diff --git a/src/test/java/net/pterodactylus/sone/core/ConfigurationSoneParserTest.java b/src/test/java/net/pterodactylus/sone/core/ConfigurationSoneParserTest.java index 4aa291b..b5e57f7 100644 --- a/src/test/java/net/pterodactylus/sone/core/ConfigurationSoneParserTest.java +++ b/src/test/java/net/pterodactylus/sone/core/ConfigurationSoneParserTest.java @@ -4,6 +4,7 @@ import static com.google.common.base.Optional.fromNullable; import static com.google.common.base.Optional.of; import static java.lang.System.currentTimeMillis; import static java.util.UUID.randomUUID; +import static net.pterodactylus.sone.Matchers.isAlbum; import static net.pterodactylus.sone.Matchers.isPost; import static net.pterodactylus.sone.Matchers.isPostReply; import static org.hamcrest.MatcherAssert.assertThat; @@ -14,23 +15,33 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; +import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidAlbumFound; +import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound; +import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.Album.Modifier; +import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.database.AlbumBuilder; +import net.pterodactylus.sone.database.AlbumBuilderFactory; import net.pterodactylus.sone.database.PostBuilder; import net.pterodactylus.sone.database.PostBuilderFactory; import net.pterodactylus.sone.database.PostReplyBuilder; @@ -314,6 +325,80 @@ public class ConfigurationSoneParserTest { setupString("Sone/1/Friends/3/ID", null); } + @Test + public void topLevelAlbumsAreParsedCorrectly() { + setupTopLevelAlbums(); + AlbumBuilderFactory albumBuilderFactory = createAlbumBuilderFactory(); + List topLevelAlbums = + configurationSoneParser.parseTopLevelAlbums( + albumBuilderFactory); + assertThat(topLevelAlbums, hasSize(2)); + Album firstAlbum = topLevelAlbums.get(0); + assertThat(firstAlbum, isAlbum("A1", null, "T1", "D1", "I1")); + assertThat(firstAlbum.getAlbums(), emptyIterable()); + assertThat(firstAlbum.getImages(), emptyIterable()); + Album secondAlbum = topLevelAlbums.get(1); + assertThat(secondAlbum, isAlbum("A2", null, "T2", "D2", null)); + assertThat(secondAlbum.getAlbums(), hasSize(1)); + assertThat(secondAlbum.getImages(), emptyIterable()); + Album thirdAlbum = secondAlbum.getAlbums().get(0); + assertThat(thirdAlbum, isAlbum("A3", "A2", "T3", "D3", "I3")); + assertThat(thirdAlbum.getAlbums(), emptyIterable()); + assertThat(thirdAlbum.getImages(), emptyIterable()); + } + + private void setupTopLevelAlbums() { + setupAlbum(0, "A1", null, "T1", "D1", "I1"); + setupAlbum(1, "A2", null, "T2", "D2", null); + setupAlbum(2, "A3", "A2", "T3", "D3", "I3"); + setupAlbum(3, null, null, null, null, null); + } + + private void setupAlbum(int albumNumber, String albumId, + String parentAlbumId, + String title, String description, String imageId) { + final String albumPrefix = "Sone/1/Albums/" + albumNumber; + setupString(albumPrefix + "/ID", albumId); + setupString(albumPrefix + "/Title", title); + setupString(albumPrefix + "/Description", description); + setupString(albumPrefix + "/Parent", parentAlbumId); + setupString(albumPrefix + "/AlbumImage", imageId); + } + + private AlbumBuilderFactory createAlbumBuilderFactory() { + AlbumBuilderFactory albumBuilderFactory = + mock(AlbumBuilderFactory.class); + when(albumBuilderFactory.newAlbumBuilder()).thenAnswer( + new Answer() { + @Override + public AlbumBuilder answer(InvocationOnMock invocation) { + return new TestAlbumBuilder(); + } + }); + return albumBuilderFactory; + } + + @Test(expected = InvalidAlbumFound.class) + public void albumWithInvalidTitleIsRecognized() { + setupAlbum(0, "A1", null, null, "D1", "I1"); + configurationSoneParser.parseTopLevelAlbums( + createAlbumBuilderFactory()); + } + + @Test(expected = InvalidAlbumFound.class) + public void albumWithInvalidDescriptionIsRecognized() { + setupAlbum(0, "A1", null, "T1", null, "I1"); + configurationSoneParser.parseTopLevelAlbums( + createAlbumBuilderFactory()); + } + + @Test(expected = InvalidParentAlbumFound.class) + public void albumWithInvalidParentIsRecognized() { + setupAlbum(0, "A1", "A0", "T1", "D1", "I1"); + configurationSoneParser.parseTopLevelAlbums( + createAlbumBuilderFactory()); + } + private static class TestValue implements Value { private final AtomicReference value = new AtomicReference(); @@ -457,4 +542,119 @@ public class ConfigurationSoneParserTest { } + private static class TestAlbumBuilder implements AlbumBuilder { + + private final Album album = mock(Album.class); + private final List albums = new ArrayList(); + private final List images = new ArrayList(); + private Album parentAlbum; + private String title; + private String description; + private String imageId; + + public TestAlbumBuilder() { + when(album.getTitle()).thenAnswer(new Answer() { + @Override + public String answer(InvocationOnMock invocation) { + return title; + } + }); + when(album.getDescription()).thenAnswer(new Answer() { + @Override + public String answer(InvocationOnMock invocation) { + return description; + } + }); + when(album.getAlbumImage()).thenAnswer(new Answer() { + @Override + public Image answer(InvocationOnMock invocation) { + if (imageId == null) { + return null; + } + Image image = mock(Image.class); + when(image.getId()).thenReturn(imageId); + return image; + } + }); + when(album.getAlbums()).thenReturn(albums); + when(album.getImages()).thenReturn(images); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + albums.add((Album) invocation.getArguments()[0]); + ((Album) invocation.getArguments()[0]).setParent(album); + return null; + } + }).when(album).addAlbum(any(Album.class)); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + images.add((Image) invocation.getArguments()[0]); + return null; + } + }).when(album).addImage(any(Image.class)); + doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + parentAlbum = (Album) invocation.getArguments()[0]; + return null; + } + }).when(album).setParent(any(Album.class)); + when(album.getParent()).thenAnswer(new Answer() { + @Override + public Album answer(InvocationOnMock invocation) { + return parentAlbum; + } + }); + when(album.modify()).thenReturn(new Modifier() { + @Override + public Modifier setTitle(String title) { + TestAlbumBuilder.this.title = title; + return this; + } + + @Override + public Modifier setDescription(String description) { + TestAlbumBuilder.this.description = description; + return this; + } + + @Override + public Modifier setAlbumImage(String imageId) { + TestAlbumBuilder.this.imageId = imageId; + return this; + } + + @Override + public Album update() throws IllegalStateException { + return album; + } + }); + } + + @Override + public AlbumBuilder randomId() { + when(album.getId()).thenReturn(randomUUID().toString()); + return this; + } + + @Override + public AlbumBuilder withId(String id) { + when(album.getId()).thenReturn(id); + return this; + } + + @Override + public AlbumBuilder by(Sone sone) { + when(album.getSone()).thenReturn(sone); + return this; + } + + @Override + public Album build() throws IllegalStateException { + return album; + } + + } + } -- 2.7.4