Merge pull request #1246 from Isira-Seneviratne/BufferedReader-lines

Use BufferedReader#lines()
This commit is contained in:
litetex 2025-01-27 22:35:03 +01:00 committed by GitHub
commit 3a33cefbc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 38 additions and 79 deletions

View File

@ -14,9 +14,12 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@ -110,7 +113,7 @@ public class YoutubeSubscriptionExtractor extends SubscriptionExtractor {
// Return it only if it has items (it exits early if it's the wrong file // Return it only if it has items (it exits early if it's the wrong file
// format), otherwise try the next file // format), otherwise try the next file
if (csvItems.size() > 0) { if (!csvItems.isEmpty()) {
return csvItems; return csvItems;
} }
} catch (final ExtractionException e) { } catch (final ExtractionException e) {
@ -138,69 +141,25 @@ public class YoutubeSubscriptionExtractor extends SubscriptionExtractor {
// The first line is always a header // The first line is always a header
// Header names are different based on the locale // Header names are different based on the locale
// Fortunately the data is always the same order no matter what locale // Fortunately the data is always the same order no matter what locale
try (var reader = new BufferedReader(new InputStreamReader(contentInputStream))) {
int currentLine = 0; return reader.lines()
String line = ""; .skip(1) // ignore header and skip first line
.map(line -> line.split(","))
try (BufferedReader br = new BufferedReader(new InputStreamReader(contentInputStream))) { .filter(values -> values.length >= 3)
final List<SubscriptionItem> subscriptionItems = new ArrayList<>(); .map(values -> {
// ignore header and skip first line
currentLine = 1;
line = br.readLine();
while ((line = br.readLine()) != null) {
currentLine++;
// Exit early if we've read the first few lines and we haven't added any items
// It's likely we're in the wrong file
if (currentLine > 5 && subscriptionItems.size() == 0) {
break;
}
// First comma
final int i1 = line.indexOf(",");
if (i1 == -1) {
continue;
}
// Second comma
final int i2 = line.indexOf(",", i1 + 1);
if (i2 == -1) {
continue;
}
// Third comma or line length
int i3 = line.indexOf(",", i2 + 1);
if (i3 == -1) {
i3 = line.length();
}
// Channel URL from second entry // Channel URL from second entry
final String channelUrl = line final String channelUrl = values[1].replace("http://", "https://");
.substring(i1 + 1, i2) return channelUrl.startsWith(BASE_CHANNEL_URL)
.replace("http://", "https://"); ? new SubscriptionItem(
if (!channelUrl.startsWith(BASE_CHANNEL_URL)) { service.getServiceId(),
continue; channelUrl,
} values[2]) // Channel title from third entry
: null;
// Channel title from third entry })
final String channelTitle = line.substring(i2 + 1, i3); .filter(Objects::nonNull)
.collect(Collectors.toUnmodifiableList());
final SubscriptionItem newItem } catch (final UncheckedIOException | IOException e) {
= new SubscriptionItem(service.getServiceId(), channelUrl, channelTitle); throw new InvalidSourceException("Error reading CSV file", e);
subscriptionItems.add(newItem);
}
return subscriptionItems;
} catch (final IOException e) {
if (line == null) {
line = "<null>";
} else if (line.length() > 10) {
line = line.substring(0, 10) + "...";
}
throw new InvalidSourceException("Error reading CSV file on line = \"" + line
+ "\", line number = " + currentLine, e);
} }
} }
} }

View File

@ -26,8 +26,7 @@ import java.util.List;
/** /**
* Test for {@link YoutubeSubscriptionExtractor} * Test for {@link YoutubeSubscriptionExtractor}
*/ */
public class YoutubeSubscriptionExtractorTest { class YoutubeSubscriptionExtractorTest {
private static YoutubeSubscriptionExtractor subscriptionExtractor; private static YoutubeSubscriptionExtractor subscriptionExtractor;
private static LinkHandlerFactory urlHandler; private static LinkHandlerFactory urlHandler;
@ -41,7 +40,7 @@ public class YoutubeSubscriptionExtractorTest {
} }
@Test @Test
public void testFromInputStream() throws Exception { void testFromInputStream() throws Exception {
final List<SubscriptionItem> subscriptionItems = subscriptionExtractor.fromInputStream( final List<SubscriptionItem> subscriptionItems = subscriptionExtractor.fromInputStream(
new FileInputStream(resolveTestResource("youtube_takeout_import_test.json"))); new FileInputStream(resolveTestResource("youtube_takeout_import_test.json")));
assertEquals(7, subscriptionItems.size()); assertEquals(7, subscriptionItems.size());
@ -55,14 +54,14 @@ public class YoutubeSubscriptionExtractorTest {
} }
@Test @Test
public void testEmptySourceException() throws Exception { void testEmptySourceException() throws Exception {
final List<SubscriptionItem> items = subscriptionExtractor.fromInputStream( final List<SubscriptionItem> items = subscriptionExtractor.fromInputStream(
new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_8))); new ByteArrayInputStream("[]".getBytes(StandardCharsets.UTF_8)));
assertTrue(items.isEmpty()); assertTrue(items.isEmpty());
} }
@Test @Test
public void testSubscriptionWithEmptyTitleInSource() throws Exception { void testSubscriptionWithEmptyTitleInSource() throws Exception {
final String source = "[{\"snippet\":{\"resourceId\":{\"channelId\":\"UCEOXxzW2vU0P-0THehuIIeg\"}}}]"; final String source = "[{\"snippet\":{\"resourceId\":{\"channelId\":\"UCEOXxzW2vU0P-0THehuIIeg\"}}}]";
final List<SubscriptionItem> items = subscriptionExtractor.fromInputStream( final List<SubscriptionItem> items = subscriptionExtractor.fromInputStream(
new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8))); new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)));
@ -74,7 +73,7 @@ public class YoutubeSubscriptionExtractorTest {
} }
@Test @Test
public void testSubscriptionWithInvalidUrlInSource() throws Exception { void testSubscriptionWithInvalidUrlInSource() throws Exception {
final String source = "[{\"snippet\":{\"resourceId\":{\"channelId\":\"gibberish\"},\"title\":\"name1\"}}," + final String source = "[{\"snippet\":{\"resourceId\":{\"channelId\":\"gibberish\"},\"title\":\"name1\"}}," +
"{\"snippet\":{\"resourceId\":{\"channelId\":\"UCEOXxzW2vU0P-0THehuIIeg\"},\"title\":\"name2\"}}]"; "{\"snippet\":{\"resourceId\":{\"channelId\":\"UCEOXxzW2vU0P-0THehuIIeg\"},\"title\":\"name2\"}}]";
final List<SubscriptionItem> items = subscriptionExtractor.fromInputStream( final List<SubscriptionItem> items = subscriptionExtractor.fromInputStream(
@ -87,8 +86,8 @@ public class YoutubeSubscriptionExtractorTest {
} }
@Test @Test
public void testInvalidSourceException() { void testInvalidSourceException() {
List<String> invalidList = Arrays.asList( final List<String> invalidList = Arrays.asList(
"<xml><notvalid></notvalid></xml>", "<xml><notvalid></notvalid></xml>",
"<opml><notvalid></notvalid></opml>", "<opml><notvalid></notvalid></opml>",
"{\"a\":\"b\"}", "{\"a\":\"b\"}",
@ -100,13 +99,14 @@ public class YoutubeSubscriptionExtractorTest {
"\uD83D\uDC28\uD83D\uDC28\uD83D\uDC28", "\uD83D\uDC28\uD83D\uDC28\uD83D\uDC28",
"gibberish"); "gibberish");
for (String invalidContent : invalidList) { for (final String invalidContent : invalidList) {
try { try {
byte[] bytes = invalidContent.getBytes(StandardCharsets.UTF_8); final byte[] bytes = invalidContent.getBytes(StandardCharsets.UTF_8);
subscriptionExtractor.fromInputStream(new ByteArrayInputStream(bytes)); subscriptionExtractor.fromInputStream(new ByteArrayInputStream(bytes));
fail("Extracting from \"" + invalidContent + "\" didn't throw an exception"); fail("Extracting from \"" + invalidContent + "\" didn't throw an exception");
} catch (final Exception e) { } catch (final Exception e) {
boolean correctType = e instanceof SubscriptionExtractor.InvalidSourceException; final boolean correctType =
e instanceof SubscriptionExtractor.InvalidSourceException;
if (!correctType) { if (!correctType) {
e.printStackTrace(); e.printStackTrace();
} }
@ -117,7 +117,7 @@ public class YoutubeSubscriptionExtractorTest {
private static void assertSubscriptionItems(final List<SubscriptionItem> subscriptionItems) private static void assertSubscriptionItems(final List<SubscriptionItem> subscriptionItems)
throws Exception { throws Exception {
assertTrue(subscriptionItems.size() > 0); assertTrue(!subscriptionItems.isEmpty());
for (final SubscriptionItem item : subscriptionItems) { for (final SubscriptionItem item : subscriptionItems) {
assertNotNull(item.getName()); assertNotNull(item.getName());
@ -128,7 +128,7 @@ public class YoutubeSubscriptionExtractorTest {
} }
@Test @Test
public void fromZipInputStream() throws Exception { void fromZipInputStream() throws Exception {
final List<String> zipPaths = Arrays.asList( final List<String> zipPaths = Arrays.asList(
"youtube_takeout_import_test_1.zip", "youtube_takeout_import_test_1.zip",
"youtube_takeout_import_test_2.zip" "youtube_takeout_import_test_2.zip"
@ -144,13 +144,13 @@ public class YoutubeSubscriptionExtractorTest {
} }
@Test @Test
public void fromCsvInputStream() throws Exception { void fromCsvInputStream() throws Exception {
final List<String> csvPaths = Arrays.asList( final List<String> csvPaths = Arrays.asList(
"youtube_takeout_import_test_1.csv", "youtube_takeout_import_test_1.csv",
"youtube_takeout_import_test_2.csv" "youtube_takeout_import_test_2.csv"
); );
for (String path : csvPaths) for (final String path : csvPaths)
{ {
final File file = resolveTestResource(path); final File file = resolveTestResource(path);
final FileInputStream fileInputStream = new FileInputStream(file); final FileInputStream fileInputStream = new FileInputStream(file);