diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03c3099..c9e1bd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,15 +9,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [1.8, 11, 17] + java: [11, 17, 21, 24] steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: java-version: ${{ matrix.java }} + distribution: 'zulu' - name: Build run: mvn -B package @@ -26,12 +27,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 24 + distribution: 'zulu' - name: Build with coverage run: mvn -B -Pcoverage clean test jacoco:report diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 642589c..1943866 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,16 +14,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Maven Central repository - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' - server-id: ossrh - server-username: MAVEN_USERNAME # env variable to use for username in release - server-password: MAVEN_PASSWORD # env variable to use for password in release + # See https://central.sonatype.org/publish/publish-portal-maven/ + server-id: central + server-username: CENTRAL_USERNAME # env variable to use for username in release + server-password: CENTRAL_PASSWORD # env variable to use for password in release gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable to use for passphrase in release @@ -37,6 +38,6 @@ jobs: mvn -B -Dpassword=${{ secrets.GITHUB_TOKEN }} release:prepare mvn -B release:perform env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }} + CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} diff --git a/CHANGELOG.md b/CHANGELOG.md index fc22de9..1b7aa5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.12.0] - 2025-06-04 +### Added +- Include OSGi metadata in jar +### Changed +- Require at least Java 11 + +## [0.11.0] - 2023-02-27 +### Changed +- Modular JAR: Require at least Java 9 and add a module descriptor (module-info), + remove no longer necessary `Automatic-Module-Name` header + +## [0.10.1] - 2022-12-23 +### Changed +- Bump maven plugin versions + ## [0.10.0] - 2018-11-12 ### Changed - Stop URLs at '`' characters too, same as < and > @@ -72,6 +87,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Initial release! +[0.12.0]: https://github.com/robinst/autolink-java/compare/autolink-0.11.0...autolink-0.12.0 +[0.11.0]: https://github.com/robinst/autolink-java/compare/autolink-0.10.1...autolink-0.11.0 +[0.10.1]: https://github.com/robinst/autolink-java/compare/autolink-0.10.0...autolink-0.10.1 [0.10.0]: https://github.com/robinst/autolink-java/compare/autolink-0.9.0...autolink-0.10.0 [0.9.0]: https://github.com/robinst/autolink-java/compare/autolink-0.8.0...autolink-0.9.0 [0.8.0]: https://github.com/robinst/autolink-java/compare/autolink-0.7.0...autolink-0.8.0 diff --git a/README.md b/README.md index 8f13891..8f58e1d 100644 --- a/README.md +++ b/README.md @@ -32,19 +32,19 @@ Thanks to [Rinku](https://github.com/vmg/rinku) for the inspiration. Usage ----- -This library is supported on Java 8 or later. It works on Android +This library is supported on Java 11 or later. It works on Android (minimum API level 19). It has no external dependencies. Maven coordinates (see -[here](https://search.maven.org/artifact/org.nibor.autolink/autolink/0.10.0/jar) +[here](https://central.sonatype.com/artifact/org.nibor.autolink/autolink/0.12.0/overview) for other build systems): ```xml org.nibor.autolink autolink - 0.10.0 + 0.12.0 ``` @@ -53,16 +53,17 @@ Extracting links: ```java import org.nibor.autolink.*; -String input = "wow, so example: http://test.com"; -LinkExtractor linkExtractor = LinkExtractor.builder() - .linkTypes(EnumSet.of(LinkType.URL, LinkType.WWW, LinkType.EMAIL)) +var input = "two links: https://test.com and https://example.com"; +var linkExtractor = LinkExtractor.builder() + .linkTypes(EnumSet.of(LinkType.URL)) // limit to URLs .build(); -Iterable links = linkExtractor.extractLinks(input); -LinkSpan link = links.iterator().next(); -link.getType(); // LinkType.URL -link.getBeginIndex(); // 17 -link.getEndIndex(); // 32 -input.substring(link.getBeginIndex(), link.getEndIndex()); // "http://test.com" +var links = new ArrayList<>(); +for (var span : linkExtractor.extractLinks(input)) { + var link = input.substring(span.getBeginIndex(), span.getEndIndex()); + links.add(link); +} + +links; // List.of("https://test.com", "https://example.com") ``` Note that by default all supported types of links are extracted. If @@ -208,6 +209,6 @@ See CONTRIBUTING.md file. License ------- -Copyright (c) 2015-2022 Robin Stocker and others, see Git history +Copyright (c) 2015, Robin Stocker and others, see Git history MIT licensed, see LICENSE file. diff --git a/pom.xml b/pom.xml index d02fa83..ddf6190 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.nibor.autolink autolink - 0.10.1 + 0.12.1-SNAPSHOT autolink-java @@ -20,9 +20,9 @@ - junit - junit - 4.13.1 + org.junit.jupiter + junit-jupiter + 5.13.0 test @@ -34,32 +34,56 @@ + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.felix + maven-bundle-plugin + + 5.1.9 + + + + !java + + + {local-packages} + + + + + + bundle-manifest + process-classes + + manifest + + + + + + org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.14.0 - 7 - 7 - - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - - org.nibor.autolink - - + 11 com.github.siom79.japicmp japicmp-maven-plugin - 0.15.7 + 0.23.1 @@ -95,22 +119,22 @@ + - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 true - ossrh - https://oss.sonatype.org/ - true - 10 + central + true + published org.apache.maven.plugins maven-release-plugin - 3.0.0-M7 + 3.1.1 true false @@ -118,6 +142,10 @@ deploy + + org.apache.felix + maven-bundle-plugin + @@ -140,7 +168,7 @@ org.jacoco jacoco-maven-plugin - 0.8.2 + 0.8.13 prepare-agent @@ -160,7 +188,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.1 attach-sources @@ -173,7 +201,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.1 + 3.11.2 attach-javadocs @@ -189,7 +217,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.2.7 sign-artifacts @@ -211,17 +239,9 @@ - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - Robin Stocker - robin@nibor.org https://github.com/robinst/ @@ -237,7 +257,7 @@ scm:git:git@github.com:robinst/autolink-java.git scm:git:https://github.com/robinst/autolink-java.git https://github.com/robinst/autolink-java - autolink-0.10.1 + HEAD diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000..382d3a7 --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,3 @@ +module org.nibor.autolink { + exports org.nibor.autolink; +} diff --git a/src/main/java/org/nibor/autolink/LinkExtractor.java b/src/main/java/org/nibor/autolink/LinkExtractor.java index 8229571..0312eec 100644 --- a/src/main/java/org/nibor/autolink/LinkExtractor.java +++ b/src/main/java/org/nibor/autolink/LinkExtractor.java @@ -90,6 +90,17 @@ public static class Builder { private Builder() { } + /** + * @param linkTypes the link types that should be extracted (by default, all types are extracted) + * @return this builder + */ + public Builder linkTypes(LinkType... linkTypes) { + if (linkTypes == null) { + throw new NullPointerException("linkTypes must not be null"); + } + return this.linkTypes(Set.of(linkTypes)); + } + /** * @param linkTypes the link types that should be extracted (by default, all types are extracted) * @return this builder diff --git a/src/test/java/org/nibor/autolink/AutolinkEmailTest.java b/src/test/java/org/nibor/autolink/AutolinkEmailTest.java index a5a93c5..9004cab 100644 --- a/src/test/java/org/nibor/autolink/AutolinkEmailTest.java +++ b/src/test/java/org/nibor/autolink/AutolinkEmailTest.java @@ -1,35 +1,35 @@ package org.nibor.autolink; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.Parameter; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; import java.util.EnumSet; +import java.util.Set; +import java.util.stream.Stream; -@RunWith(Parameterized.class) +import static org.junit.jupiter.params.provider.Arguments.arguments; + +@ParameterizedClass +@MethodSource("data") public class AutolinkEmailTest extends AutolinkTestCase { - @Parameters(name = "{2}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {LinkExtractor.builder().linkTypes(EnumSet.of(LinkType.EMAIL)).build(), true, "email"}, - {LinkExtractor.builder().linkTypes(EnumSet.allOf(LinkType.class)).build(), true, "all"}, - {LinkExtractor.builder().emailDomainMustHaveDot(false).build(), false, "all, single part domain"} - }); + public static Stream data() { + return Stream.of( + arguments(EnumSet.of(LinkType.EMAIL), true), + arguments(EnumSet.allOf(LinkType.class), true), + arguments(EnumSet.allOf(LinkType.class), false) + ); } @Parameter(0) - public LinkExtractor linkExtractor; + public Set linkTypes; @Parameter(1) public boolean domainMustHaveDot; - @Parameter(2) - public String description; - @Test public void notLinked() { assertNotLinked(""); @@ -136,7 +136,7 @@ public void replyLevel() { @Override protected LinkExtractor getLinkExtractor() { - return linkExtractor; + return LinkExtractor.builder().linkTypes(linkTypes).emailDomainMustHaveDot(domainMustHaveDot).build(); } private void assertLinked(String input, String expected) { diff --git a/src/test/java/org/nibor/autolink/AutolinkTestCase.java b/src/test/java/org/nibor/autolink/AutolinkTestCase.java index ace5367..ecae3ab 100644 --- a/src/test/java/org/nibor/autolink/AutolinkTestCase.java +++ b/src/test/java/org/nibor/autolink/AutolinkTestCase.java @@ -1,6 +1,7 @@ package org.nibor.autolink; -import static org.junit.Assert.assertEquals; + +import static org.junit.jupiter.api.Assertions.assertEquals; public abstract class AutolinkTestCase { diff --git a/src/test/java/org/nibor/autolink/AutolinkUrlTest.java b/src/test/java/org/nibor/autolink/AutolinkUrlTest.java index ec40c4c..4f20dbc 100644 --- a/src/test/java/org/nibor/autolink/AutolinkUrlTest.java +++ b/src/test/java/org/nibor/autolink/AutolinkUrlTest.java @@ -1,32 +1,31 @@ package org.nibor.autolink; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.Parameter; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; import java.util.EnumSet; +import java.util.Set; +import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.params.provider.Arguments.arguments; -@RunWith(Parameterized.class) +@ParameterizedClass +@MethodSource("data") public class AutolinkUrlTest extends AutolinkTestCase { - @Parameters(name = "{1}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {LinkExtractor.builder().linkTypes(EnumSet.of(LinkType.URL)).build(), "URL"}, - {LinkExtractor.builder().linkTypes(EnumSet.allOf(LinkType.class)).build(), "all"} - }); + public static Stream data() { + return Stream.of( + arguments(EnumSet.of(LinkType.URL)), + arguments(EnumSet.allOf(LinkType.class)) + ); } - @Parameter(0) - public LinkExtractor linkExtractor; - - @Parameter(1) - public String description; + @Parameter + public Set linkTypes; @Test public void notLinked() { @@ -197,24 +196,24 @@ public void international() { @Test public void unicodeWhitespace() { - char[] whitespace = new char[] { - '\u00A0', // no-break space - '\u2000', // en quad - '\u2001', // em quad - '\u2002', // en space - '\u2003', // em space - '\u2004', // three-per-em space - '\u2005', // four-per-em space - '\u2006', // six-per-em space - '\u2007', // figure space - '\u2008', // punctuation space - '\u2009', // thin space - '\u200A', // hair space - '\u2028', // line separator - '\u2029', // paragraph separator - '\u202F', // narrow no-break space - '\u205F', // medium mathematical space - '\u3000', // ideographic space + char[] whitespace = new char[]{ + '\u00A0', // no-break space + '\u2000', // en quad + '\u2001', // em quad + '\u2002', // en space + '\u2003', // em space + '\u2004', // three-per-em space + '\u2005', // four-per-em space + '\u2006', // six-per-em space + '\u2007', // figure space + '\u2008', // punctuation space + '\u2009', // thin space + '\u200A', // hair space + '\u2028', // line separator + '\u2029', // paragraph separator + '\u202F', // narrow no-break space + '\u205F', // medium mathematical space + '\u3000', // ideographic space }; for (char c : whitespace) { @@ -242,7 +241,7 @@ public void linkToString() { @Override protected LinkExtractor getLinkExtractor() { - return linkExtractor; + return LinkExtractor.builder().linkTypes(linkTypes).build(); } protected void assertLinked(String input, String expected) { diff --git a/src/test/java/org/nibor/autolink/AutolinkWwwTest.java b/src/test/java/org/nibor/autolink/AutolinkWwwTest.java index 8453f22..f85a916 100644 --- a/src/test/java/org/nibor/autolink/AutolinkWwwTest.java +++ b/src/test/java/org/nibor/autolink/AutolinkWwwTest.java @@ -1,30 +1,31 @@ package org.nibor.autolink; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; -import java.util.Arrays; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.Parameter; +import org.junit.jupiter.params.ParameterizedClass; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import java.util.EnumSet; +import java.util.Set; +import java.util.stream.Stream; + +import static org.junit.jupiter.params.provider.Arguments.arguments; -@RunWith(Parameterized.class) +@ParameterizedClass +@MethodSource("data") public class AutolinkWwwTest extends AutolinkTestCase { - @Parameters(name = "{1}") - public static Iterable data() { - return Arrays.asList(new Object[][]{ - {LinkExtractor.builder().linkTypes(EnumSet.of(LinkType.WWW)).build(), "WWW"}, - {LinkExtractor.builder().linkTypes(EnumSet.allOf(LinkType.class)).build(), "all"} - }); + public static Stream data() { + return Stream.of( + arguments(EnumSet.of(LinkType.WWW)), + arguments(EnumSet.allOf(LinkType.class)) + ); } - @Parameter(0) - public LinkExtractor linkExtractor; - - @Parameter(1) - public String description; + @Parameter + public Set linkTypes; @Test public void notLinked() { @@ -82,7 +83,7 @@ public void replyLevel() { @Override protected LinkExtractor getLinkExtractor() { - return linkExtractor; + return LinkExtractor.builder().linkTypes(linkTypes).build(); } private void assertLinked(String input, String expected) { diff --git a/src/test/java/org/nibor/autolink/LinkIterableTest.java b/src/test/java/org/nibor/autolink/LinkIterableTest.java index 45fded3..3b3b207 100644 --- a/src/test/java/org/nibor/autolink/LinkIterableTest.java +++ b/src/test/java/org/nibor/autolink/LinkIterableTest.java @@ -1,11 +1,11 @@ package org.nibor.autolink; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Iterator; import java.util.NoSuchElementException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class LinkIterableTest { @@ -27,18 +27,18 @@ public void hasNextOnlyAdvancesOnce() { assertFalse(iterator.hasNext()); } - @Test(expected = NoSuchElementException.class) + @Test public void nextThrowsNoSuchElementException() { Iterable iterable = getSingleLinkIterable(); Iterator iterator = iterable.iterator(); assertNotNull(iterator.next()); - iterator.next(); + assertThrows(NoSuchElementException.class, iterator::next); } - @Test(expected = UnsupportedOperationException.class) + @Test public void removeUnsupported() { Iterable iterable = getSingleLinkIterable(); - iterable.iterator().remove(); + assertThrows(UnsupportedOperationException.class, () -> iterable.iterator().remove()); } private Iterable getSingleLinkIterable() { diff --git a/src/test/java/org/nibor/autolink/SpanIterableTest.java b/src/test/java/org/nibor/autolink/SpanIterableTest.java index dc00b1d..c9ffd09 100644 --- a/src/test/java/org/nibor/autolink/SpanIterableTest.java +++ b/src/test/java/org/nibor/autolink/SpanIterableTest.java @@ -1,10 +1,10 @@ package org.nibor.autolink; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.*; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class SpanIterableTest { @@ -40,18 +40,18 @@ public void spanAndLinkSequences() { extractSpansAsText("http://example.org https://example.com")); } - @Test(expected = NoSuchElementException.class) + @Test public void nextThrowsNoSuchElementException() { Iterable iterable = extractSpans("test"); Iterator iterator = iterable.iterator(); assertNotNull(iterator.next()); - iterator.next(); + assertThrows(NoSuchElementException.class, iterator::next); } - @Test(expected = UnsupportedOperationException.class) + @Test public void removeUnsupported() { Iterable iterable = extractSpans("test"); - iterable.iterator().remove(); + assertThrows(UnsupportedOperationException.class, () -> iterable.iterator().remove()); } private Iterable extractSpans(String input) { diff --git a/src/test/java/org/nibor/autolink/UsageExampleTest.java b/src/test/java/org/nibor/autolink/UsageExampleTest.java index 69e890c..aa983ac 100644 --- a/src/test/java/org/nibor/autolink/UsageExampleTest.java +++ b/src/test/java/org/nibor/autolink/UsageExampleTest.java @@ -1,10 +1,12 @@ package org.nibor.autolink; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class UsageExampleTest { @@ -35,6 +37,21 @@ public void linkify() { assertEquals("wow http://test.com such linked", sb.toString()); } + @Test + public void extractLinks() { + var input = "hey https://test.com and https://example.com"; + var linkExtractor = LinkExtractor.builder() + .linkTypes(EnumSet.of(LinkType.URL)) + .build(); + var links = new ArrayList<>(); + for (var linkSpan : linkExtractor.extractLinks(input)) { + var link = input.substring(linkSpan.getBeginIndex(), linkSpan.getEndIndex()); + links.add(link); + } + + assertEquals(List.of("https://test.com", "https://example.com"), links); + } + // Mocked here to not have to depend on owasp-java-encoder in tests private static class Encode { public static String forHtmlAttribute(String text) {