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;
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)*)\\}");
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()) {
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;
}
public boolean isText() {
return false;
}
public boolean isInlineTag() {
return false;
}
public boolean isLineBreak() {
return false;
}
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;
}
public Tag getTag() {
return tag;
}
}
private static final Pattern LINK_REFERENCE_PATTERN = Pattern.compile(
"^\\s*(" +
Helper.LINK_REFERENCE_PATTERN.pattern() +
")");
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;
};
}
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();
}
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);
}
}
}