package io.vertx.codegen.doc;

import io.vertx.codegen.Helper;
import io.vertx.codegen.type.TypeMirrorFactory;

import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

A comment token.
Author:Julien Viet
/** * A comment token. * * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */
public abstract class Token { private static final Pattern TOKEN_SPLITTER = Pattern.compile("(\\{@\\p{Alpha}[^\\}]*\\})|(\\r?\\n)"); private static final Pattern INLINE_TAG_PATTERN = Pattern.compile("\\{@([^\\p{javaWhitespace}]+)((?:.|\\n)*)\\}");
Tokenize the string.
Params:
  • s – the string to tokenize
Returns:the tokens after analysis
/** * Tokenize the string. * * @param s the string to tokenize * @return the tokens after analysis */
public static List<Token> tokenize(String s) { ArrayList<Token> events = new ArrayList<>(); parse(s, 0, TOKEN_SPLITTER.matcher(s), events); return events; } private static void parse(String s, int prev, Matcher matcher, ArrayList<Token> events) { if (matcher.find()) { if (matcher.start() > prev) { events.add(new Token.Text(s.substring(prev, matcher.start()))); } String value = s.substring(matcher.start(), matcher.end()); if (matcher.group(1) != null) { Matcher tagMatcher = INLINE_TAG_PATTERN.matcher(value); if (!tagMatcher.matches()) { // If we are here, it means the INLINE_TAG_PATTERN matches less then TOKEN_SPLITTER and this is a bug. // so we should know at least the value that raised it throw new AssertionError("bug -->" + value + "<--"); } events.add(new Token.InlineTag(value, new Tag(tagMatcher.group(1), tagMatcher.group(2)))); } else { events.add(new Token.LineBreak(value)); } parse(s, matcher.end(), matcher, events); } else { if (prev < s.length()) { events.add(new Token.Text(s.substring(prev, s.length()))); } } } final String value; public Token(String value) { this.value = value; }
Returns:true if the token is text
/** * @return true if the token is text */
public boolean isText() { return false; }
Returns:true if the token is an inline tag
/** * @return true if the token is an inline tag */
public boolean isInlineTag() { return false; }
Returns:true if the token is line break
/** * @return true if the token is line break */
public boolean isLineBreak() { return false; }
Returns:the token text
/** * @return the token text */
public String getValue() { return value; } public static class Text extends Token { public Text(String value) { super(value); } @Override public boolean isText() { return true; } } public static class LineBreak extends Token { public LineBreak(String value) { super(value); } @Override public boolean isLineBreak() { return true; } } public static class InlineTag extends Token { private final Tag tag; public InlineTag(String value, Tag tag) { super(value); this.tag = tag; } @Override public boolean isInlineTag() { return true; }
Returns:the parsed tag
/** * @return the parsed tag */
public Tag getTag() { return tag; } } // Slight modification to accomodate left whitespace trimming private static final Pattern LINK_REFERENCE_PATTERN = Pattern.compile( "^\\s*(" + Helper.LINK_REFERENCE_PATTERN.pattern() + ")");
Create a tag mapper that remaps tags with extra contexutal info like @link tags.
Params:
  • elementUtils – the element utils
  • typeUtils – the type utils
  • ownerElt – the type element in which this tag is declared
Returns:the mapper
/** * Create a tag mapper that remaps tags with extra contexutal info like @link tags. * * @param elementUtils the element utils * @param typeUtils the type utils * @param ownerElt the type element in which this tag is declared * @return the mapper */
public static Function<Token, Token> tagMapper( Elements elementUtils, Types typeUtils, TypeElement ownerElt) { TypeMirrorFactory typeFactory = new TypeMirrorFactory(elementUtils, typeUtils); return token -> { if (token.isInlineTag()) { Tag tag = ((Token.InlineTag) token).getTag(); if (tag.getName().equals("link")) { Matcher matcher = LINK_REFERENCE_PATTERN.matcher(tag.getValue()); if (matcher.find()) { Element resolvedElt = Helper.resolveSignature( elementUtils, typeUtils, ownerElt, matcher.group(1)); if (resolvedElt != null) { TypeElement resolvedTypeElt = Helper.getElementTypeOf(resolvedElt); if (resolvedTypeElt != null) { DeclaredType resolvedType = (DeclaredType) resolvedTypeElt.asType(); Tag.Link tagLink = new Tag.Link( tag.getValue(), resolvedElt, typeFactory.create(resolvedType), tag.getValue().substring(matcher.end())); token = new Token.InlineTag(token.getValue(), tagLink); } } } } } return token; }; }
Render a list of tokens to HTML markup and return it.
Params:
  • tokens – the tokens to render
  • margin – the left margin
  • linkToHtml – the function that renders a tag link to HTML markup
  • sep – the separator
Returns:the rendered HTML
/** * Render a list of tokens to HTML markup and return it. * * @param tokens the tokens to render * @param margin the left margin * @param linkToHtml the function that renders a tag link to HTML markup * @param sep the separator * @return the rendered HTML */
public static String toHtml(List<Token> tokens, String margin, Function<Tag.Link, String> linkToHtml, String sep) { StringWriter sw = new StringWriter(); toHtml(tokens, margin, linkToHtml, sep, new PrintWriter(sw)); return sw.toString(); }
Render a list of tokens to HTML markup.
Params:
  • tokens – the tokens to render
  • margin – the left margin
  • linkToHtml – the function that renders a tag link to HTML markup
  • sep – the separator
  • writer – the writer for output
/** * Render a list of tokens to HTML markup. * * @param tokens the tokens to render * @param margin the left margin * @param linkToHtml the function that renders a tag link to HTML markup * @param sep the separator * @param writer the writer for output */
public static void toHtml(List<Token> tokens, String margin, Function<Tag.Link, String> linkToHtml, String sep, PrintWriter writer) { boolean need = true; for (Token token : tokens) { if (need) { writer.append(margin); need = false; } if (token.isLineBreak()) { writer.append(sep); need = true; } else if (token.isText()) { writer.append(token.getValue()); } else { Tag tag = ((Token.InlineTag) token).getTag(); if (tag instanceof Tag.Link) { Tag.Link tagLink = (Tag.Link) tag; String link = linkToHtml.apply((Tag.Link) tag); if (link == null || link.trim().isEmpty()) { link = tagLink.getLabel(); } if (link == null || link.trim().isEmpty()) { link = tagLink.targetElement.getSimpleName().toString(); } writer.append(link); } else if (tag.getName().equals("code")) { writer.append("<code>").append(tag.value.trim()).append("</code>"); } } } if (!need) { writer.append(sep); } } }