+ private int findEndOfLink(String line) {
+ Matcher matcher = whitespacePattern.matcher(line);
+ if (!matcher.find(0)) {
+ return line.length();
+ }
+ int nextWhitespace = matcher.start();
+ int lastPunctuation = nextWhitespace;
+ while (isPunctuation(line.charAt(lastPunctuation - 1))) {
+ lastPunctuation -= 1;
+ }
+ if (lastPunctuation < nextWhitespace) {
+ return lastPunctuation;
+ }
+ int openParens = 0;
+ for (int i = 0; i < nextWhitespace; i++) {
+ switch (line.charAt(i)) {
+ case '(':
+ openParens++;
+ break;
+ case ')':
+ openParens--;
+ if (openParens < 0) {
+ return i;
+ }
+ default:
+ }
+ }
+ return nextWhitespace;
+ }
+
+ private boolean isPunctuation(char character) {
+ return (character == '.') || (character == ',');
+ }
+
+ private static class NextLink {
+
+ private final int position;
+ private final LinkType linkType;
+
+ private NextLink(int position, LinkType linkType) {
+ this.position = position;
+ this.linkType = linkType;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public LinkType getLinkType() {
+ return linkType;
+ }
+
+ public static Optional<NextLink> findNextLink(String line) {
+ int earliestLinkPosition = Integer.MAX_VALUE;
+ LinkType linkType = null;
+ for (LinkType possibleLinkType : LinkType.values()) {
+ int nextLinkPosition = line.indexOf(possibleLinkType.getScheme());
+ if (nextLinkPosition > -1) {
+ if (nextLinkPosition < earliestLinkPosition) {
+ earliestLinkPosition = nextLinkPosition;
+ linkType = possibleLinkType;
+ }
+ }
+ }
+ return earliestLinkPosition < Integer.MAX_VALUE ?
+ Optional.of(new NextLink(earliestLinkPosition, linkType)) : Optional.<NextLink>absent();
+ }
+
+ }
+