| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.google.googlejavaformat.java.javadoc; |
| |
| import static com.google.googlejavaformat.java.javadoc.JavadocLexer.lex; |
| import static com.google.googlejavaformat.java.javadoc.Token.Type.BR_TAG; |
| import static com.google.googlejavaformat.java.javadoc.Token.Type.PARAGRAPH_OPEN_TAG; |
| import static java.util.regex.Pattern.CASE_INSENSITIVE; |
| import static java.util.regex.Pattern.compile; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.googlejavaformat.java.javadoc.JavadocLexer.LexException; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Entry point for formatting Javadoc. |
| * |
| * <p>This stateless class reads tokens from the stateful lexer and translates them to "requests" |
| * and "writes" to the stateful writer. It also munges tokens into "standardized" forms. Finally, it |
| * performs postprocessing to convert the written Javadoc to a one-liner if possible or to leave a |
| * single blank line if it's empty. |
| */ |
| public final class JavadocFormatter { |
| |
| static final int MAX_LINE_LENGTH = 100; |
| |
| /** |
| * Formats the given Javadoc comment, which must start with ∕✱✱ and end with ✱∕. The output will |
| * start and end with the same characters. |
| */ |
| public static String formatJavadoc(String input, int blockIndent) { |
| ImmutableList<Token> tokens; |
| try { |
| tokens = lex(input); |
| } catch (LexException e) { |
| return input; |
| } |
| String result = render(tokens, blockIndent); |
| return makeSingleLineIfPossible(blockIndent, result); |
| } |
| |
| private static String render(List<Token> input, int blockIndent) { |
| JavadocWriter output = new JavadocWriter(blockIndent); |
| for (Token token : input) { |
| switch (token.getType()) { |
| case BEGIN_JAVADOC: |
| output.writeBeginJavadoc(); |
| break; |
| case END_JAVADOC: |
| output.writeEndJavadoc(); |
| return output.toString(); |
| case FOOTER_JAVADOC_TAG_START: |
| output.writeFooterJavadocTagStart(token); |
| break; |
| case LIST_OPEN_TAG: |
| output.writeListOpen(token); |
| break; |
| case LIST_CLOSE_TAG: |
| output.writeListClose(token); |
| break; |
| case LIST_ITEM_OPEN_TAG: |
| output.writeListItemOpen(token); |
| break; |
| case HEADER_OPEN_TAG: |
| output.writeHeaderOpen(token); |
| break; |
| case HEADER_CLOSE_TAG: |
| output.writeHeaderClose(token); |
| break; |
| case PARAGRAPH_OPEN_TAG: |
| output.writeParagraphOpen(standardizePToken(token)); |
| break; |
| case BLOCKQUOTE_OPEN_TAG: |
| case BLOCKQUOTE_CLOSE_TAG: |
| output.writeBlockquoteOpenOrClose(token); |
| break; |
| case PRE_OPEN_TAG: |
| output.writePreOpen(token); |
| break; |
| case PRE_CLOSE_TAG: |
| output.writePreClose(token); |
| break; |
| case CODE_OPEN_TAG: |
| output.writeCodeOpen(token); |
| break; |
| case CODE_CLOSE_TAG: |
| output.writeCodeClose(token); |
| break; |
| case TABLE_OPEN_TAG: |
| output.writeTableOpen(token); |
| break; |
| case TABLE_CLOSE_TAG: |
| output.writeTableClose(token); |
| break; |
| case MOE_BEGIN_STRIP_COMMENT: |
| output.requestMoeBeginStripComment(token); |
| break; |
| case MOE_END_STRIP_COMMENT: |
| output.writeMoeEndStripComment(token); |
| break; |
| case HTML_COMMENT: |
| output.writeHtmlComment(token); |
| break; |
| case BR_TAG: |
| output.writeBr(standardizeBrToken(token)); |
| break; |
| case WHITESPACE: |
| output.requestWhitespace(); |
| break; |
| case FORCED_NEWLINE: |
| output.writeLineBreakNoAutoIndent(); |
| break; |
| case LITERAL: |
| output.writeLiteral(token); |
| break; |
| case PARAGRAPH_CLOSE_TAG: |
| case LIST_ITEM_CLOSE_TAG: |
| case OPTIONAL_LINE_BREAK: |
| break; |
| default: |
| throw new AssertionError(token.getType()); |
| } |
| } |
| throw new AssertionError(); |
| } |
| |
| /* |
| * TODO(cpovirk): Is this really the right location for the standardize* methods? Maybe the lexer |
| * should include them as part of its own postprocessing? Or even the writer could make sense. |
| */ |
| |
| private static Token standardizeBrToken(Token token) { |
| return standardize(token, STANDARD_BR_TOKEN); |
| } |
| |
| private static Token standardizePToken(Token token) { |
| return standardize(token, STANDARD_P_TOKEN); |
| } |
| |
| private static Token standardize(Token token, Token standardToken) { |
| return SIMPLE_TAG_PATTERN.matcher(token.getValue()).matches() ? standardToken : token; |
| } |
| |
| private static final Token STANDARD_BR_TOKEN = new Token(BR_TAG, "<br>"); |
| private static final Token STANDARD_P_TOKEN = new Token(PARAGRAPH_OPEN_TAG, "<p>"); |
| private static final Pattern SIMPLE_TAG_PATTERN = compile("^<\\w+\\s*/?\\s*>", CASE_INSENSITIVE); |
| |
| private static final Pattern ONE_CONTENT_LINE_PATTERN = compile(" */[*][*]\n *[*] (.*)\n *[*]/"); |
| |
| /** |
| * Returns the given string or a one-line version of it (e.g., "∕✱✱ Tests for foos. ✱∕") if it |
| * fits on one line. |
| */ |
| private static String makeSingleLineIfPossible(int blockIndent, String input) { |
| int oneLinerContentLength = MAX_LINE_LENGTH - "/** */".length() - blockIndent; |
| Matcher matcher = ONE_CONTENT_LINE_PATTERN.matcher(input); |
| if (matcher.matches() && matcher.group(1).isEmpty()) { |
| return "/** */"; |
| } else if (matcher.matches() && matcher.group(1).length() <= oneLinerContentLength) { |
| return "/** " + matcher.group(1) + " */"; |
| } |
| return input; |
| } |
| |
| private JavadocFormatter() {} |
| } |